pensiev 0.25.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. memos/__init__.py +6 -0
  2. memos/cmds/__init__.py +0 -0
  3. memos/cmds/library.py +1289 -0
  4. memos/cmds/plugin.py +96 -0
  5. memos/commands.py +865 -0
  6. memos/config.py +225 -0
  7. memos/crud.py +605 -0
  8. memos/databases/__init__.py +0 -0
  9. memos/databases/initializers.py +481 -0
  10. memos/dataset_extractor_for_florence.py +165 -0
  11. memos/dataset_extractor_for_internvl2.py +192 -0
  12. memos/default_config.yaml +88 -0
  13. memos/embedding.py +129 -0
  14. memos/frame_extractor.py +53 -0
  15. memos/logging_config.py +35 -0
  16. memos/main.py +104 -0
  17. memos/migrations/alembic/README +1 -0
  18. memos/migrations/alembic/__pycache__/env.cpython-310.pyc +0 -0
  19. memos/migrations/alembic/env.py +108 -0
  20. memos/migrations/alembic/script.py.mako +30 -0
  21. memos/migrations/alembic/versions/00904ac8c6fc_add_indexes_to_entitymodel.py +63 -0
  22. memos/migrations/alembic/versions/04acdaf75664_add_indices_to_entitytags_and_metadata.py +86 -0
  23. memos/migrations/alembic/versions/12504c5b1d3c_add_extra_columns_for_embedding.py +67 -0
  24. memos/migrations/alembic/versions/31a1ad0e10b3_add_entity_plugin_status.py +71 -0
  25. memos/migrations/alembic/versions/__pycache__/00904ac8c6fc_add_indexes_to_entitymodel.cpython-310.pyc +0 -0
  26. memos/migrations/alembic/versions/__pycache__/04acdaf75664_add_indices_to_entitytags_and_metadata.cpython-310.pyc +0 -0
  27. memos/migrations/alembic/versions/__pycache__/12504c5b1d3c_add_extra_columns_for_embedding.cpython-310.pyc +0 -0
  28. memos/migrations/alembic/versions/__pycache__/20f5ecab014d_add_entity_plugin_status.cpython-310.pyc +0 -0
  29. memos/migrations/alembic/versions/__pycache__/31a1ad0e10b3_add_entity_plugin_status.cpython-310.pyc +0 -0
  30. memos/migrations/alembic/versions/__pycache__/4fcb062c5128_add_extra_columns_for_embedding.cpython-310.pyc +0 -0
  31. memos/migrations/alembic/versions/__pycache__/d10c55fbb7d2_add_index_for_entity_file_type_group_.cpython-310.pyc +0 -0
  32. memos/migrations/alembic/versions/__pycache__/f8f158182416_add_active_app_index.cpython-310.pyc +0 -0
  33. memos/migrations/alembic/versions/d10c55fbb7d2_add_index_for_entity_file_type_group_.py +44 -0
  34. memos/migrations/alembic/versions/f8f158182416_add_active_app_index.py +75 -0
  35. memos/migrations/alembic.ini +116 -0
  36. memos/migrations.py +19 -0
  37. memos/models.py +199 -0
  38. memos/plugins/__init__.py +0 -0
  39. memos/plugins/ocr/__init__.py +0 -0
  40. memos/plugins/ocr/main.py +251 -0
  41. memos/plugins/ocr/models/ch_PP-OCRv4_det_infer.onnx +0 -0
  42. memos/plugins/ocr/models/ch_PP-OCRv4_rec_infer.onnx +0 -0
  43. memos/plugins/ocr/models/ch_ppocr_mobile_v2.0_cls_train.onnx +0 -0
  44. memos/plugins/ocr/ppocr-gpu.yaml +43 -0
  45. memos/plugins/ocr/ppocr.yaml +44 -0
  46. memos/plugins/ocr/server.py +227 -0
  47. memos/plugins/ocr/temp_ppocr.yaml +42 -0
  48. memos/plugins/vlm/__init__.py +0 -0
  49. memos/plugins/vlm/main.py +251 -0
  50. memos/prepare_dataset.py +107 -0
  51. memos/process_webp.py +55 -0
  52. memos/read_metadata.py +32 -0
  53. memos/record.py +358 -0
  54. memos/schemas.py +289 -0
  55. memos/search.py +1198 -0
  56. memos/server.py +883 -0
  57. memos/shotsum.py +105 -0
  58. memos/shotsum_with_ocr.py +145 -0
  59. memos/simple_tokenizer/dict/README.md +31 -0
  60. memos/simple_tokenizer/dict/hmm_model.utf8 +34 -0
  61. memos/simple_tokenizer/dict/idf.utf8 +258826 -0
  62. memos/simple_tokenizer/dict/jieba.dict.utf8 +348982 -0
  63. memos/simple_tokenizer/dict/pos_dict/char_state_tab.utf8 +6653 -0
  64. memos/simple_tokenizer/dict/pos_dict/prob_emit.utf8 +166 -0
  65. memos/simple_tokenizer/dict/pos_dict/prob_start.utf8 +259 -0
  66. memos/simple_tokenizer/dict/pos_dict/prob_trans.utf8 +5222 -0
  67. memos/simple_tokenizer/dict/stop_words.utf8 +1534 -0
  68. memos/simple_tokenizer/dict/user.dict.utf8 +4 -0
  69. memos/simple_tokenizer/linux/libsimple.so +0 -0
  70. memos/simple_tokenizer/macos/libsimple.dylib +0 -0
  71. memos/simple_tokenizer/windows/simple.dll +0 -0
  72. memos/static/_app/immutable/assets/0.e250c031.css +1 -0
  73. memos/static/_app/immutable/assets/_layout.e7937cfe.css +1 -0
  74. memos/static/_app/immutable/chunks/index.5c08976b.js +1 -0
  75. memos/static/_app/immutable/chunks/index.60ee613b.js +4 -0
  76. memos/static/_app/immutable/chunks/runtime.a7926cf6.js +5 -0
  77. memos/static/_app/immutable/chunks/scheduler.5c1cff6e.js +1 -0
  78. memos/static/_app/immutable/chunks/singletons.583bdf4e.js +1 -0
  79. memos/static/_app/immutable/entry/app.666c1643.js +1 -0
  80. memos/static/_app/immutable/entry/start.aed5c701.js +3 -0
  81. memos/static/_app/immutable/nodes/0.5862ea38.js +7 -0
  82. memos/static/_app/immutable/nodes/1.35378a5e.js +1 -0
  83. memos/static/_app/immutable/nodes/2.1ccf9ea5.js +81 -0
  84. memos/static/_app/version.json +1 -0
  85. memos/static/app.html +36 -0
  86. memos/static/favicon.png +0 -0
  87. memos/static/logos/memos_logo_1024.png +0 -0
  88. memos/static/logos/memos_logo_1024@2x.png +0 -0
  89. memos/static/logos/memos_logo_128.png +0 -0
  90. memos/static/logos/memos_logo_128@2x.png +0 -0
  91. memos/static/logos/memos_logo_16.png +0 -0
  92. memos/static/logos/memos_logo_16@2x.png +0 -0
  93. memos/static/logos/memos_logo_256.png +0 -0
  94. memos/static/logos/memos_logo_256@2x.png +0 -0
  95. memos/static/logos/memos_logo_32.png +0 -0
  96. memos/static/logos/memos_logo_32@2x.png +0 -0
  97. memos/static/logos/memos_logo_512.png +0 -0
  98. memos/static/logos/memos_logo_512@2x.png +0 -0
  99. memos/static/logos/memos_logo_64.png +0 -0
  100. memos/static/logos/memos_logo_64@2x.png +0 -0
  101. memos/test_server.py +802 -0
  102. memos/utils.py +49 -0
  103. memos_ml_backends/florence2_server.py +176 -0
  104. memos_ml_backends/qwen2vl_server.py +182 -0
  105. memos_ml_backends/schemas.py +48 -0
  106. pensiev-0.25.5.dist-info/LICENSE +201 -0
  107. pensiev-0.25.5.dist-info/METADATA +541 -0
  108. pensiev-0.25.5.dist-info/RECORD +111 -0
  109. pensiev-0.25.5.dist-info/WHEEL +5 -0
  110. pensiev-0.25.5.dist-info/entry_points.txt +2 -0
  111. pensiev-0.25.5.dist-info/top_level.txt +2 -0
@@ -0,0 +1,108 @@
1
+ from logging.config import fileConfig
2
+ from sqlalchemy import engine_from_config
3
+ from sqlalchemy import pool
4
+ from alembic import context
5
+ import sqlite_vec
6
+ from sqlalchemy import event
7
+ from urllib.parse import urlparse
8
+
9
+ from memos import models
10
+ from memos import config as memosConfig
11
+
12
+ # this is the Alembic Config object, which provides
13
+ # access to the values within the .ini file in use.
14
+ config = context.config
15
+
16
+ # Interpret the config file for Python logging.
17
+ # This line sets up loggers basically.
18
+ if config.config_file_name is not None:
19
+ fileConfig(config.config_file_name)
20
+
21
+ # add your model's MetaData object here
22
+ # for 'autogenerate' support
23
+ target_metadata = models.RawBase.metadata
24
+
25
+ # overwrite the desired value
26
+ config.set_main_option("sqlalchemy.url", memosConfig.settings.database_url)
27
+
28
+ def get_db_type():
29
+ """Get database type from URL."""
30
+ url = config.get_main_option("sqlalchemy.url")
31
+ return urlparse(url).scheme
32
+
33
+ def configure_sqlite(engine):
34
+ """Configure SQLite specific settings."""
35
+ def load_extension(dbapi_conn, connection_record):
36
+ dbapi_conn.enable_load_extension(True)
37
+ sqlite_vec.load(dbapi_conn)
38
+
39
+ event.listen(engine, 'connect', load_extension)
40
+
41
+ def run_migrations_offline() -> None:
42
+ """Run migrations in 'offline' mode.
43
+
44
+ This configures the context with just a URL
45
+ and not an Engine, though an Engine is acceptable
46
+ here as well. By skipping the Engine creation
47
+ we don't even need a DBAPI to be available.
48
+
49
+ Calls to context.execute() here emit the given string to the
50
+ script output.
51
+
52
+ """
53
+ url = config.get_main_option("sqlalchemy.url")
54
+ context.configure(
55
+ url=url,
56
+ target_metadata=target_metadata,
57
+ literal_binds=True,
58
+ dialect_opts={"paramstyle": "named"},
59
+ )
60
+
61
+ with context.begin_transaction():
62
+ context.run_migrations()
63
+
64
+ def run_migrations_online() -> None:
65
+ """Run migrations in 'online' mode."""
66
+ # Configure the database connection based on the database type
67
+ db_type = get_db_type()
68
+
69
+ # Set appropriate connection parameters based on database type
70
+ if db_type == 'postgresql':
71
+ engine_config = {
72
+ 'pool_size': 5,
73
+ 'max_overflow': 10,
74
+ 'pool_timeout': 30,
75
+ 'pool_recycle': 3600,
76
+ }
77
+ else: # sqlite
78
+ engine_config = {
79
+ 'connect_args': {'timeout': 60},
80
+ 'poolclass': pool.NullPool, # SQLite doesn't need connection pooling
81
+ }
82
+
83
+ connectable = engine_from_config(
84
+ config.get_section(config.config_ini_section, {}),
85
+ prefix="sqlalchemy.",
86
+ **engine_config
87
+ )
88
+
89
+ # Configure SQLite extensions if needed
90
+ if db_type == 'sqlite':
91
+ configure_sqlite(connectable)
92
+
93
+ with connectable.connect() as connection:
94
+ context.configure(
95
+ connection=connection,
96
+ target_metadata=target_metadata,
97
+ # Add PostgreSQL-specific options
98
+ compare_type=True, # Compare column types
99
+ compare_server_default=True, # Compare default values
100
+ )
101
+
102
+ with context.begin_transaction():
103
+ context.run_migrations()
104
+
105
+ if context.is_offline_mode():
106
+ run_migrations_offline()
107
+ else:
108
+ run_migrations_online()
@@ -0,0 +1,30 @@
1
+ """${message}
2
+
3
+ Revision ID: ${up_revision}
4
+ Revises: ${down_revision | comma,n}
5
+ Create Date: ${create_date}
6
+
7
+ """
8
+ from typing import Sequence, Union
9
+ from alembic import op
10
+ import sqlalchemy as sa
11
+ from urllib.parse import urlparse
12
+ ${imports if imports else ""}
13
+
14
+ # revision identifiers, used by Alembic.
15
+ revision: str = ${repr(up_revision)}
16
+ down_revision: Union[str, None] = ${repr(down_revision)}
17
+ branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)}
18
+ depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}
19
+
20
+ def get_db_type():
21
+ config = op.get_context().config
22
+ url = config.get_main_option("sqlalchemy.url")
23
+ return urlparse(url).scheme
24
+
25
+ def upgrade() -> None:
26
+ ${upgrades if upgrades else "pass"}
27
+
28
+
29
+ def downgrade() -> None:
30
+ ${downgrades if downgrades else "pass"}
@@ -0,0 +1,63 @@
1
+ """Add indexes to EntityModel
2
+
3
+ Revision ID: 00904ac8c6fc
4
+ Revises:
5
+ Create Date: 2024-07-17 12:16:59.911285
6
+
7
+ """
8
+ from typing import Sequence, Union
9
+
10
+ from alembic import op
11
+ import sqlalchemy as sa
12
+ from sqlalchemy import inspect
13
+
14
+
15
+ # revision identifiers, used by Alembic.
16
+ revision: str = '00904ac8c6fc'
17
+ down_revision: Union[str, None] = None
18
+ branch_labels: Union[str, Sequence[str], None] = None
19
+ depends_on: Union[str, Sequence[str], None] = None
20
+
21
+
22
+ def upgrade() -> None:
23
+ # 获取 inspector
24
+ conn = op.get_bind()
25
+ inspector = inspect(conn)
26
+ existing_indexes = inspector.get_indexes('entities')
27
+ existing_index_names = [idx['name'] for idx in existing_indexes]
28
+
29
+ # 要创建的索引列表
30
+ indexes = [
31
+ ('idx_file_type', ['file_type']),
32
+ ('idx_filename', ['filename']),
33
+ ('idx_filepath', ['filepath']),
34
+ ('idx_folder_id', ['folder_id']),
35
+ ('idx_library_id', ['library_id']),
36
+ ]
37
+
38
+ # 只创建不存在的索引
39
+ for idx_name, columns in indexes:
40
+ if idx_name not in existing_index_names:
41
+ op.create_index(idx_name, 'entities', columns, unique=False)
42
+
43
+
44
+ def downgrade() -> None:
45
+ # 获取 inspector
46
+ conn = op.get_bind()
47
+ inspector = inspect(conn)
48
+ existing_indexes = inspector.get_indexes('entities')
49
+ existing_index_names = [idx['name'] for idx in existing_indexes]
50
+
51
+ # 要删除的索引列表
52
+ indexes = [
53
+ 'idx_library_id',
54
+ 'idx_folder_id',
55
+ 'idx_filepath',
56
+ 'idx_filename',
57
+ 'idx_file_type',
58
+ ]
59
+
60
+ # 只删除存在的索引
61
+ for idx_name in indexes:
62
+ if idx_name in existing_index_names:
63
+ op.drop_index(idx_name, table_name='entities')
@@ -0,0 +1,86 @@
1
+ """Add indices to EntityTags and Metadata
2
+
3
+ Revision ID: 04acdaf75664
4
+ Revises: 00904ac8c6fc
5
+ Create Date: 2024-08-14 12:18:46.039436
6
+
7
+ """
8
+ from typing import Sequence, Union
9
+
10
+ from alembic import op
11
+ import sqlalchemy as sa
12
+ from sqlalchemy import inspect
13
+
14
+
15
+ # revision identifiers, used by Alembic.
16
+ revision: str = '04acdaf75664'
17
+ down_revision: Union[str, None] = '00904ac8c6fc'
18
+ branch_labels: Union[str, Sequence[str], None] = None
19
+ depends_on: Union[str, Sequence[str], None] = None
20
+
21
+
22
+ def upgrade() -> None:
23
+ # 获取 inspector
24
+ conn = op.get_bind()
25
+ inspector = inspect(conn)
26
+
27
+ # 检查 entity_tags 表的索引
28
+ entity_tags_indexes = inspector.get_indexes('entity_tags')
29
+ entity_tags_index_names = [idx['name'] for idx in entity_tags_indexes]
30
+
31
+ # 检查 metadata_entries 表的索引
32
+ metadata_indexes = inspector.get_indexes('metadata_entries')
33
+ metadata_index_names = [idx['name'] for idx in metadata_indexes]
34
+
35
+ # 创建 entity_tags 的索引
36
+ entity_tags_indices = [
37
+ ('idx_entity_tag_entity_id', ['entity_id']),
38
+ ('idx_entity_tag_tag_id', ['tag_id']),
39
+ ]
40
+
41
+ for idx_name, columns in entity_tags_indices:
42
+ if idx_name not in entity_tags_index_names:
43
+ op.create_index(idx_name, 'entity_tags', columns, unique=False)
44
+
45
+ # 创建 metadata_entries 的索引
46
+ metadata_indices = [
47
+ ('idx_metadata_entity_id', ['entity_id']),
48
+ ('idx_metadata_key', ['key']),
49
+ ]
50
+
51
+ for idx_name, columns in metadata_indices:
52
+ if idx_name not in metadata_index_names:
53
+ op.create_index(idx_name, 'metadata_entries', columns, unique=False)
54
+
55
+
56
+ def downgrade() -> None:
57
+ # 获取 inspector
58
+ conn = op.get_bind()
59
+ inspector = inspect(conn)
60
+
61
+ # 检查现有索引
62
+ entity_tags_indexes = inspector.get_indexes('entity_tags')
63
+ entity_tags_index_names = [idx['name'] for idx in entity_tags_indexes]
64
+
65
+ metadata_indexes = inspector.get_indexes('metadata_entries')
66
+ metadata_index_names = [idx['name'] for idx in metadata_indexes]
67
+
68
+ # 删除 metadata_entries 的索引
69
+ metadata_indices = [
70
+ 'idx_metadata_key',
71
+ 'idx_metadata_entity_id',
72
+ ]
73
+
74
+ for idx_name in metadata_indices:
75
+ if idx_name in metadata_index_names:
76
+ op.drop_index(idx_name, table_name='metadata_entries')
77
+
78
+ # 删除 entity_tags 的索引
79
+ entity_tags_indices = [
80
+ 'idx_entity_tag_tag_id',
81
+ 'idx_entity_tag_entity_id',
82
+ ]
83
+
84
+ for idx_name in entity_tags_indices:
85
+ if idx_name in entity_tags_index_names:
86
+ op.drop_index(idx_name, table_name='entity_tags')
@@ -0,0 +1,67 @@
1
+ """add_extra_columns_for_embedding
2
+
3
+ Revision ID: 12504c5b1d3c
4
+ Revises: f8f158182416
5
+ Create Date: 2025-01-02 10:11:48.997145
6
+
7
+ """
8
+ from typing import Sequence, Union
9
+ from urllib.parse import urlparse
10
+
11
+ from alembic import op
12
+ import sqlalchemy as sa
13
+ from memos.config import settings
14
+ import sqlite_vec
15
+ from sqlalchemy import inspect
16
+
17
+
18
+ # revision identifiers, used by Alembic.
19
+ revision: str = '12504c5b1d3c'
20
+ down_revision: Union[str, None] = 'f8f158182416'
21
+ branch_labels: Union[str, Sequence[str], None] = None
22
+ depends_on: Union[str, Sequence[str], None] = None
23
+
24
+
25
+ def get_db_type():
26
+ config = op.get_context().config
27
+ url = config.get_main_option("sqlalchemy.url")
28
+ return urlparse(url).scheme
29
+
30
+
31
+ def upgrade() -> None:
32
+ if get_db_type() != 'sqlite':
33
+ return
34
+
35
+ conn = op.get_bind()
36
+ inspector = inspect(conn)
37
+ tables = inspector.get_table_names()
38
+
39
+ if 'entities_vec_v2' not in tables:
40
+ conn = op.get_bind()
41
+
42
+ print("Loading sqlite_vec extension...")
43
+ conn.connection.enable_load_extension(True)
44
+ sqlite_vec.load(conn.connection)
45
+ print("sqlite_vec extension loaded successfully")
46
+
47
+ print("Creating entities_vec_v2 table...")
48
+ conn.execute(
49
+ sa.text(
50
+ f"""
51
+ CREATE VIRTUAL TABLE IF NOT EXISTS entities_vec_v2 USING vec0(
52
+ embedding float[{settings.embedding.num_dim}] distance_metric=cosine,
53
+ file_type_group text,
54
+ created_at_timestamp integer,
55
+ file_created_at_timestamp integer,
56
+ file_created_at_date text partition key,
57
+ app_name text,
58
+ library_id integer
59
+ )
60
+ """
61
+ )
62
+ )
63
+ print("Table entities_vec_v2 created successfully")
64
+
65
+
66
+ def downgrade() -> None:
67
+ pass
@@ -0,0 +1,71 @@
1
+ """add_entity_plugin_status
2
+
3
+ Revision ID: 31a1ad0e10b3
4
+ Revises: 12504c5b1d3c
5
+ Create Date: 2025-01-03 13:47:44.291335
6
+
7
+ """
8
+ from typing import Sequence, Union
9
+
10
+ from alembic import op
11
+ import sqlalchemy as sa
12
+
13
+
14
+ # revision identifiers, used by Alembic.
15
+ revision: str = '31a1ad0e10b3'
16
+ down_revision: Union[str, None] = '12504c5b1d3c'
17
+ branch_labels: Union[str, Sequence[str], None] = None
18
+ depends_on: Union[str, Sequence[str], None] = None
19
+
20
+
21
+ def upgrade() -> None:
22
+ # 创建 entity_plugin_status 表
23
+ op.create_table(
24
+ 'entity_plugin_status',
25
+ sa.Column('entity_id', sa.Integer(), nullable=False),
26
+ sa.Column('plugin_id', sa.Integer(), nullable=False),
27
+ sa.Column('processed_at', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=False),
28
+ sa.ForeignKeyConstraint(['entity_id'], ['entities.id'], ondelete='CASCADE'),
29
+ sa.ForeignKeyConstraint(['plugin_id'], ['plugins.id'], ondelete='CASCADE'),
30
+ sa.PrimaryKeyConstraint('entity_id', 'plugin_id'),
31
+ if_not_exists=True
32
+ )
33
+
34
+ # 创建索引
35
+ op.create_index('idx_entity_plugin_entity_id', 'entity_plugin_status', ['entity_id'], if_not_exists=True)
36
+ op.create_index('idx_entity_plugin_plugin_id', 'entity_plugin_status', ['plugin_id'], if_not_exists=True)
37
+
38
+ # 迁移现有数据
39
+ # 注意:使用 execute() 执行原始 SQL
40
+ op.execute("""
41
+ INSERT INTO entity_plugin_status (entity_id, plugin_id, processed_at)
42
+ SELECT DISTINCT
43
+ e.id as entity_id,
44
+ p.id as plugin_id,
45
+ e.last_scan_at as processed_at
46
+ FROM entities e
47
+ JOIN metadata_entries me ON e.id = me.entity_id
48
+ JOIN plugins p ON p.name = 'builtin_ocr'
49
+ WHERE me.source = 'ocr'
50
+ """)
51
+
52
+ op.execute("""
53
+ INSERT INTO entity_plugin_status (entity_id, plugin_id, processed_at)
54
+ SELECT DISTINCT
55
+ e.id as entity_id,
56
+ p.id as plugin_id,
57
+ e.last_scan_at as processed_at
58
+ FROM entities e
59
+ JOIN metadata_entries me ON e.id = me.entity_id
60
+ JOIN plugins p ON p.name = 'builtin_vlm'
61
+ WHERE me.source = 'vlm'
62
+ """)
63
+
64
+
65
+ def downgrade() -> None:
66
+ # 删除索引
67
+ op.drop_index('idx_entity_plugin_plugin_id', table_name='entity_plugin_status')
68
+ op.drop_index('idx_entity_plugin_entity_id', table_name='entity_plugin_status')
69
+
70
+ # 删除表
71
+ op.drop_table('entity_plugin_status')
@@ -0,0 +1,44 @@
1
+ """Add index for entity file_type_group and file_created_at
2
+
3
+ Revision ID: d10c55fbb7d2
4
+ Revises: 31a1ad0e10b3
5
+ Create Date: 2025-01-20 23:59:42.021204
6
+
7
+ """
8
+ from typing import Sequence, Union
9
+
10
+ from alembic import op
11
+ import sqlalchemy as sa
12
+ from sqlalchemy import inspect
13
+
14
+ # revision identifiers, used by Alembic.
15
+ revision: str = 'd10c55fbb7d2'
16
+ down_revision: Union[str, None] = '31a1ad0e10b3'
17
+ branch_labels: Union[str, Sequence[str], None] = None
18
+ depends_on: Union[str, Sequence[str], None] = None
19
+
20
+
21
+ def upgrade() -> None:
22
+ conn = op.get_bind()
23
+ inspector = inspect(conn)
24
+ existing_indexes = inspector.get_indexes('entities')
25
+ existing_index_names = [idx['name'] for idx in existing_indexes]
26
+
27
+ if 'idx_file_type_group' not in existing_index_names:
28
+ op.create_index("idx_file_type_group", "entities", ["file_type_group"])
29
+
30
+ if 'idx_file_created_at' not in existing_index_names:
31
+ op.create_index("idx_file_created_at", "entities", ["file_created_at"])
32
+
33
+
34
+ def downgrade() -> None:
35
+ conn = op.get_bind()
36
+ inspector = inspect(conn)
37
+ existing_indexes = inspector.get_indexes('entities')
38
+ existing_index_names = [idx['name'] for idx in existing_indexes]
39
+
40
+ if 'idx_file_type_group' in existing_index_names:
41
+ op.drop_index("idx_file_type_group", "entities")
42
+
43
+ if 'idx_file_created_at' in existing_index_names:
44
+ op.drop_index("idx_file_created_at", "entities")
@@ -0,0 +1,75 @@
1
+ """add_active_app_index
2
+
3
+ Revision ID: f8f158182416
4
+ Revises: 04acdaf75664
5
+ Create Date: 2024-12-30 14:42:06.165967
6
+
7
+ """
8
+ from typing import Sequence, Union
9
+ from urllib.parse import urlparse
10
+
11
+ from alembic import op
12
+ import sqlalchemy as sa
13
+ import sqlite_vec
14
+ from sqlalchemy import inspect
15
+
16
+
17
+ # revision identifiers, used by Alembic.
18
+ revision: str = 'f8f158182416'
19
+ down_revision: Union[str, None] = '04acdaf75664'
20
+ branch_labels: Union[str, Sequence[str], None] = None
21
+ depends_on: Union[str, Sequence[str], None] = None
22
+
23
+
24
+ def get_db_type():
25
+ config = op.get_context().config
26
+ url = config.get_main_option("sqlalchemy.url")
27
+ return urlparse(url).scheme
28
+
29
+
30
+ def upgrade() -> None:
31
+ if get_db_type() != 'sqlite':
32
+ return
33
+
34
+ # 获取 bind 连接
35
+ conn = op.get_bind()
36
+
37
+ # 直接在连接上加载扩展
38
+ conn.connection.enable_load_extension(True)
39
+ sqlite_vec.load(conn.connection)
40
+
41
+ # 检查索引是否存在
42
+ inspector = inspect(conn)
43
+ existing_indexes = inspector.get_indexes('metadata_entries')
44
+ existing_index_names = [idx['name'] for idx in existing_indexes]
45
+
46
+ # 如果索引不存在,才创建
47
+ if 'idx_metadata_active_app' not in existing_index_names:
48
+ conn.execute(
49
+ sa.text('''
50
+ CREATE INDEX idx_metadata_active_app
51
+ ON metadata_entries(key, entity_id, value)
52
+ WHERE key = 'active_app'
53
+ ''')
54
+ )
55
+
56
+
57
+ def downgrade() -> None:
58
+ if get_db_type() != 'sqlite':
59
+ return
60
+
61
+ # 获取连接
62
+ conn = op.get_bind()
63
+
64
+ # 直接在连接上加载扩展
65
+ conn.connection.enable_load_extension(True)
66
+ sqlite_vec.load(conn.connection)
67
+
68
+ # 检查索引是否存在
69
+ inspector = inspect(conn)
70
+ existing_indexes = inspector.get_indexes('metadata_entries')
71
+ existing_index_names = [idx['name'] for idx in existing_indexes]
72
+
73
+ # 如果索引存在,才删除
74
+ if 'idx_metadata_active_app' in existing_index_names:
75
+ conn.execute(sa.text('DROP INDEX idx_metadata_active_app'))
@@ -0,0 +1,116 @@
1
+ # A generic, single database configuration.
2
+
3
+ [alembic]
4
+ # path to migration scripts
5
+ # Use forward slashes (/) also on windows to provide an os agnostic path
6
+ script_location = %(here)s/alembic
7
+
8
+ # template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
9
+ # Uncomment the line below if you want the files to be prepended with date and time
10
+ # see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file
11
+ # for all available tokens
12
+ # file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s
13
+
14
+ # sys.path path, will be prepended to sys.path if present.
15
+ # defaults to the current working directory.
16
+ prepend_sys_path = .
17
+
18
+ # timezone to use when rendering the date within the migration file
19
+ # as well as the filename.
20
+ # If specified, requires the python>=3.9 or backports.zoneinfo library.
21
+ # Any required deps can installed by adding `alembic[tz]` to the pip requirements
22
+ # string value is passed to ZoneInfo()
23
+ # leave blank for localtime
24
+ # timezone =
25
+
26
+ # max length of characters to apply to the "slug" field
27
+ # truncate_slug_length = 40
28
+
29
+ # set to 'true' to run the environment during
30
+ # the 'revision' command, regardless of autogenerate
31
+ # revision_environment = false
32
+
33
+ # set to 'true' to allow .pyc and .pyo files without
34
+ # a source .py file to be detected as revisions in the
35
+ # versions/ directory
36
+ # sourceless = false
37
+
38
+ # version location specification; This defaults
39
+ # to alembic/versions. When using multiple version
40
+ # directories, initial revisions must be specified with --version-path.
41
+ # The path separator used here should be the separator specified by "version_path_separator" below.
42
+ # version_locations = %(here)s/bar:%(here)s/bat:alembic/versions
43
+
44
+ # version path separator; As mentioned above, this is the character used to split
45
+ # version_locations. The default within new alembic.ini files is "os", which uses os.pathsep.
46
+ # If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas.
47
+ # Valid values for version_path_separator are:
48
+ #
49
+ # version_path_separator = :
50
+ # version_path_separator = ;
51
+ # version_path_separator = space
52
+ version_path_separator = os # Use os.pathsep. Default configuration used for new projects.
53
+
54
+ # set to 'true' to search source files recursively
55
+ # in each "version_locations" directory
56
+ # new in Alembic version 1.10
57
+ # recursive_version_locations = false
58
+
59
+ # the output encoding used when revision files
60
+ # are written from script.py.mako
61
+ # output_encoding = utf-8
62
+
63
+ # sqlalchemy.url = sqlite:///~/.memos/database.db
64
+
65
+
66
+ [post_write_hooks]
67
+ # post_write_hooks defines scripts or Python functions that are run
68
+ # on newly generated revision scripts. See the documentation for further
69
+ # detail and examples
70
+
71
+ # format using "black" - use the console_scripts runner, against the "black" entrypoint
72
+ # hooks = black
73
+ # black.type = console_scripts
74
+ # black.entrypoint = black
75
+ # black.options = -l 79 REVISION_SCRIPT_FILENAME
76
+
77
+ # lint with attempts to fix using "ruff" - use the exec runner, execute a binary
78
+ # hooks = ruff
79
+ # ruff.type = exec
80
+ # ruff.executable = %(here)s/.venv/bin/ruff
81
+ # ruff.options = --fix REVISION_SCRIPT_FILENAME
82
+
83
+ # Logging configuration
84
+ [loggers]
85
+ keys = root,sqlalchemy,alembic
86
+
87
+ [handlers]
88
+ keys = console
89
+
90
+ [formatters]
91
+ keys = generic
92
+
93
+ [logger_root]
94
+ level = WARN
95
+ handlers = console
96
+ qualname =
97
+
98
+ [logger_sqlalchemy]
99
+ level = WARN
100
+ handlers =
101
+ qualname = sqlalchemy.engine
102
+
103
+ [logger_alembic]
104
+ level = INFO
105
+ handlers =
106
+ qualname = alembic
107
+
108
+ [handler_console]
109
+ class = StreamHandler
110
+ args = (sys.stderr,)
111
+ level = NOTSET
112
+ formatter = generic
113
+
114
+ [formatter_generic]
115
+ format = %(levelname)-5.5s [%(name)s] %(message)s
116
+ datefmt = %H:%M:%S