basic-memory 0.7.0__py3-none-any.whl → 0.17.4__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.
Potentially problematic release.
This version of basic-memory might be problematic. Click here for more details.
- basic_memory/__init__.py +5 -1
- basic_memory/alembic/alembic.ini +119 -0
- basic_memory/alembic/env.py +130 -20
- basic_memory/alembic/migrations.py +4 -9
- basic_memory/alembic/versions/314f1ea54dc4_add_postgres_full_text_search_support_.py +131 -0
- basic_memory/alembic/versions/502b60eaa905_remove_required_from_entity_permalink.py +51 -0
- basic_memory/alembic/versions/5fe1ab1ccebe_add_projects_table.py +120 -0
- basic_memory/alembic/versions/647e7a75e2cd_project_constraint_fix.py +112 -0
- basic_memory/alembic/versions/6830751f5fb6_merge_multiple_heads.py +24 -0
- basic_memory/alembic/versions/9d9c1cb7d8f5_add_mtime_and_size_columns_to_entity_.py +49 -0
- basic_memory/alembic/versions/a1b2c3d4e5f6_fix_project_foreign_keys.py +49 -0
- basic_memory/alembic/versions/a2b3c4d5e6f7_add_search_index_entity_cascade.py +56 -0
- basic_memory/alembic/versions/b3c3938bacdb_relation_to_name_unique_index.py +44 -0
- basic_memory/alembic/versions/cc7172b46608_update_search_index_schema.py +113 -0
- basic_memory/alembic/versions/e7e1f4367280_add_scan_watermark_tracking_to_project.py +37 -0
- basic_memory/alembic/versions/f8a9b2c3d4e5_add_pg_trgm_for_fuzzy_link_resolution.py +239 -0
- basic_memory/alembic/versions/g9a0b3c4d5e6_add_external_id_to_project_and_entity.py +173 -0
- basic_memory/api/app.py +87 -20
- basic_memory/api/container.py +133 -0
- basic_memory/api/routers/__init__.py +4 -1
- basic_memory/api/routers/directory_router.py +84 -0
- basic_memory/api/routers/importer_router.py +152 -0
- basic_memory/api/routers/knowledge_router.py +180 -23
- basic_memory/api/routers/management_router.py +80 -0
- basic_memory/api/routers/memory_router.py +9 -64
- basic_memory/api/routers/project_router.py +460 -0
- basic_memory/api/routers/prompt_router.py +260 -0
- basic_memory/api/routers/resource_router.py +136 -11
- basic_memory/api/routers/search_router.py +5 -5
- basic_memory/api/routers/utils.py +169 -0
- basic_memory/api/template_loader.py +292 -0
- basic_memory/api/v2/__init__.py +35 -0
- basic_memory/api/v2/routers/__init__.py +21 -0
- basic_memory/api/v2/routers/directory_router.py +93 -0
- basic_memory/api/v2/routers/importer_router.py +181 -0
- basic_memory/api/v2/routers/knowledge_router.py +427 -0
- basic_memory/api/v2/routers/memory_router.py +130 -0
- basic_memory/api/v2/routers/project_router.py +359 -0
- basic_memory/api/v2/routers/prompt_router.py +269 -0
- basic_memory/api/v2/routers/resource_router.py +286 -0
- basic_memory/api/v2/routers/search_router.py +73 -0
- basic_memory/cli/app.py +80 -10
- basic_memory/cli/auth.py +300 -0
- basic_memory/cli/commands/__init__.py +15 -2
- basic_memory/cli/commands/cloud/__init__.py +6 -0
- basic_memory/cli/commands/cloud/api_client.py +127 -0
- basic_memory/cli/commands/cloud/bisync_commands.py +110 -0
- basic_memory/cli/commands/cloud/cloud_utils.py +108 -0
- basic_memory/cli/commands/cloud/core_commands.py +195 -0
- basic_memory/cli/commands/cloud/rclone_commands.py +397 -0
- basic_memory/cli/commands/cloud/rclone_config.py +110 -0
- basic_memory/cli/commands/cloud/rclone_installer.py +263 -0
- basic_memory/cli/commands/cloud/upload.py +240 -0
- basic_memory/cli/commands/cloud/upload_command.py +124 -0
- basic_memory/cli/commands/command_utils.py +99 -0
- basic_memory/cli/commands/db.py +87 -12
- basic_memory/cli/commands/format.py +198 -0
- basic_memory/cli/commands/import_chatgpt.py +47 -223
- basic_memory/cli/commands/import_claude_conversations.py +48 -171
- basic_memory/cli/commands/import_claude_projects.py +53 -160
- basic_memory/cli/commands/import_memory_json.py +55 -111
- basic_memory/cli/commands/mcp.py +67 -11
- basic_memory/cli/commands/project.py +889 -0
- basic_memory/cli/commands/status.py +52 -34
- basic_memory/cli/commands/telemetry.py +81 -0
- basic_memory/cli/commands/tool.py +341 -0
- basic_memory/cli/container.py +84 -0
- basic_memory/cli/main.py +14 -6
- basic_memory/config.py +580 -26
- basic_memory/db.py +285 -28
- basic_memory/deps/__init__.py +293 -0
- basic_memory/deps/config.py +26 -0
- basic_memory/deps/db.py +56 -0
- basic_memory/deps/importers.py +200 -0
- basic_memory/deps/projects.py +238 -0
- basic_memory/deps/repositories.py +179 -0
- basic_memory/deps/services.py +480 -0
- basic_memory/deps.py +16 -185
- basic_memory/file_utils.py +318 -54
- basic_memory/ignore_utils.py +297 -0
- basic_memory/importers/__init__.py +27 -0
- basic_memory/importers/base.py +100 -0
- basic_memory/importers/chatgpt_importer.py +245 -0
- basic_memory/importers/claude_conversations_importer.py +192 -0
- basic_memory/importers/claude_projects_importer.py +184 -0
- basic_memory/importers/memory_json_importer.py +128 -0
- basic_memory/importers/utils.py +61 -0
- basic_memory/markdown/entity_parser.py +182 -23
- basic_memory/markdown/markdown_processor.py +70 -7
- basic_memory/markdown/plugins.py +43 -23
- basic_memory/markdown/schemas.py +1 -1
- basic_memory/markdown/utils.py +38 -14
- basic_memory/mcp/async_client.py +135 -4
- basic_memory/mcp/clients/__init__.py +28 -0
- basic_memory/mcp/clients/directory.py +70 -0
- basic_memory/mcp/clients/knowledge.py +176 -0
- basic_memory/mcp/clients/memory.py +120 -0
- basic_memory/mcp/clients/project.py +89 -0
- basic_memory/mcp/clients/resource.py +71 -0
- basic_memory/mcp/clients/search.py +65 -0
- basic_memory/mcp/container.py +110 -0
- basic_memory/mcp/project_context.py +155 -0
- basic_memory/mcp/prompts/__init__.py +19 -0
- basic_memory/mcp/prompts/ai_assistant_guide.py +70 -0
- basic_memory/mcp/prompts/continue_conversation.py +62 -0
- basic_memory/mcp/prompts/recent_activity.py +188 -0
- basic_memory/mcp/prompts/search.py +57 -0
- basic_memory/mcp/prompts/utils.py +162 -0
- basic_memory/mcp/resources/ai_assistant_guide.md +283 -0
- basic_memory/mcp/resources/project_info.py +71 -0
- basic_memory/mcp/server.py +61 -9
- basic_memory/mcp/tools/__init__.py +33 -21
- basic_memory/mcp/tools/build_context.py +120 -0
- basic_memory/mcp/tools/canvas.py +152 -0
- basic_memory/mcp/tools/chatgpt_tools.py +190 -0
- basic_memory/mcp/tools/delete_note.py +249 -0
- basic_memory/mcp/tools/edit_note.py +325 -0
- basic_memory/mcp/tools/list_directory.py +157 -0
- basic_memory/mcp/tools/move_note.py +549 -0
- basic_memory/mcp/tools/project_management.py +204 -0
- basic_memory/mcp/tools/read_content.py +281 -0
- basic_memory/mcp/tools/read_note.py +265 -0
- basic_memory/mcp/tools/recent_activity.py +528 -0
- basic_memory/mcp/tools/search.py +377 -24
- basic_memory/mcp/tools/utils.py +402 -16
- basic_memory/mcp/tools/view_note.py +78 -0
- basic_memory/mcp/tools/write_note.py +230 -0
- basic_memory/models/__init__.py +3 -2
- basic_memory/models/knowledge.py +82 -17
- basic_memory/models/project.py +93 -0
- basic_memory/models/search.py +68 -8
- basic_memory/project_resolver.py +222 -0
- basic_memory/repository/__init__.py +2 -0
- basic_memory/repository/entity_repository.py +437 -8
- basic_memory/repository/observation_repository.py +36 -3
- basic_memory/repository/postgres_search_repository.py +451 -0
- basic_memory/repository/project_info_repository.py +10 -0
- basic_memory/repository/project_repository.py +140 -0
- basic_memory/repository/relation_repository.py +79 -4
- basic_memory/repository/repository.py +148 -29
- basic_memory/repository/search_index_row.py +95 -0
- basic_memory/repository/search_repository.py +79 -268
- basic_memory/repository/search_repository_base.py +241 -0
- basic_memory/repository/sqlite_search_repository.py +437 -0
- basic_memory/runtime.py +61 -0
- basic_memory/schemas/__init__.py +22 -9
- basic_memory/schemas/base.py +131 -12
- basic_memory/schemas/cloud.py +50 -0
- basic_memory/schemas/directory.py +31 -0
- basic_memory/schemas/importer.py +35 -0
- basic_memory/schemas/memory.py +194 -25
- basic_memory/schemas/project_info.py +213 -0
- basic_memory/schemas/prompt.py +90 -0
- basic_memory/schemas/request.py +56 -2
- basic_memory/schemas/response.py +85 -28
- basic_memory/schemas/search.py +36 -35
- basic_memory/schemas/sync_report.py +72 -0
- basic_memory/schemas/v2/__init__.py +27 -0
- basic_memory/schemas/v2/entity.py +133 -0
- basic_memory/schemas/v2/resource.py +47 -0
- basic_memory/services/__init__.py +2 -1
- basic_memory/services/context_service.py +451 -138
- basic_memory/services/directory_service.py +310 -0
- basic_memory/services/entity_service.py +636 -71
- basic_memory/services/exceptions.py +21 -0
- basic_memory/services/file_service.py +402 -33
- basic_memory/services/initialization.py +216 -0
- basic_memory/services/link_resolver.py +50 -56
- basic_memory/services/project_service.py +888 -0
- basic_memory/services/search_service.py +232 -37
- basic_memory/sync/__init__.py +4 -2
- basic_memory/sync/background_sync.py +26 -0
- basic_memory/sync/coordinator.py +160 -0
- basic_memory/sync/sync_service.py +1200 -109
- basic_memory/sync/watch_service.py +432 -135
- basic_memory/telemetry.py +249 -0
- basic_memory/templates/prompts/continue_conversation.hbs +110 -0
- basic_memory/templates/prompts/search.hbs +101 -0
- basic_memory/utils.py +407 -54
- basic_memory-0.17.4.dist-info/METADATA +617 -0
- basic_memory-0.17.4.dist-info/RECORD +193 -0
- {basic_memory-0.7.0.dist-info → basic_memory-0.17.4.dist-info}/WHEEL +1 -1
- {basic_memory-0.7.0.dist-info → basic_memory-0.17.4.dist-info}/entry_points.txt +1 -0
- basic_memory/alembic/README +0 -1
- basic_memory/cli/commands/sync.py +0 -206
- basic_memory/cli/commands/tools.py +0 -157
- basic_memory/mcp/tools/knowledge.py +0 -68
- basic_memory/mcp/tools/memory.py +0 -170
- basic_memory/mcp/tools/notes.py +0 -202
- basic_memory/schemas/discovery.py +0 -28
- basic_memory/sync/file_change_scanner.py +0 -158
- basic_memory/sync/utils.py +0 -31
- basic_memory-0.7.0.dist-info/METADATA +0 -378
- basic_memory-0.7.0.dist-info/RECORD +0 -82
- {basic_memory-0.7.0.dist-info → basic_memory-0.17.4.dist-info}/licenses/LICENSE +0 -0
basic_memory/db.py
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
2
4
|
from contextlib import asynccontextmanager
|
|
3
5
|
from enum import Enum, auto
|
|
4
6
|
from pathlib import Path
|
|
5
7
|
from typing import AsyncGenerator, Optional
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
from basic_memory.config import ProjectConfig
|
|
9
|
+
from basic_memory.config import BasicMemoryConfig, ConfigManager, DatabaseBackend
|
|
9
10
|
from alembic import command
|
|
10
11
|
from alembic.config import Config
|
|
11
12
|
|
|
12
13
|
from loguru import logger
|
|
13
|
-
from sqlalchemy import text
|
|
14
|
+
from sqlalchemy import text, event
|
|
14
15
|
from sqlalchemy.ext.asyncio import (
|
|
15
16
|
create_async_engine,
|
|
16
17
|
async_sessionmaker,
|
|
@@ -18,8 +19,25 @@ from sqlalchemy.ext.asyncio import (
|
|
|
18
19
|
AsyncEngine,
|
|
19
20
|
async_scoped_session,
|
|
20
21
|
)
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
from sqlalchemy.pool import NullPool
|
|
23
|
+
|
|
24
|
+
from basic_memory.repository.postgres_search_repository import PostgresSearchRepository
|
|
25
|
+
from basic_memory.repository.sqlite_search_repository import SQLiteSearchRepository
|
|
26
|
+
|
|
27
|
+
# -----------------------------------------------------------------------------
|
|
28
|
+
# Windows event loop policy
|
|
29
|
+
# -----------------------------------------------------------------------------
|
|
30
|
+
# On Windows, the default ProactorEventLoop has known rough edges with aiosqlite
|
|
31
|
+
# during shutdown/teardown (threads posting results to a loop that's closing),
|
|
32
|
+
# which can manifest as:
|
|
33
|
+
# - "RuntimeError: Event loop is closed"
|
|
34
|
+
# - "IndexError: pop from an empty deque"
|
|
35
|
+
#
|
|
36
|
+
# The SelectorEventLoop doesn't support subprocess operations, so code that uses
|
|
37
|
+
# asyncio.create_subprocess_shell() (like sync_service._quick_count_files) must
|
|
38
|
+
# detect Windows and use fallback implementations.
|
|
39
|
+
if sys.platform == "win32": # pragma: no cover
|
|
40
|
+
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
|
23
41
|
|
|
24
42
|
# Module level state
|
|
25
43
|
_engine: Optional[AsyncEngine] = None
|
|
@@ -31,10 +49,41 @@ class DatabaseType(Enum):
|
|
|
31
49
|
|
|
32
50
|
MEMORY = auto()
|
|
33
51
|
FILESYSTEM = auto()
|
|
52
|
+
POSTGRES = auto()
|
|
34
53
|
|
|
35
54
|
@classmethod
|
|
36
|
-
def get_db_url(
|
|
37
|
-
""
|
|
55
|
+
def get_db_url(
|
|
56
|
+
cls, db_path: Path, db_type: "DatabaseType", config: Optional[BasicMemoryConfig] = None
|
|
57
|
+
) -> str:
|
|
58
|
+
"""Get SQLAlchemy URL for database path.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
db_path: Path to SQLite database file (ignored for Postgres)
|
|
62
|
+
db_type: Type of database (MEMORY, FILESYSTEM, or POSTGRES)
|
|
63
|
+
config: Optional config to check for database backend and URL
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
SQLAlchemy connection URL
|
|
67
|
+
"""
|
|
68
|
+
# Load config if not provided
|
|
69
|
+
if config is None:
|
|
70
|
+
config = ConfigManager().config
|
|
71
|
+
|
|
72
|
+
# Handle explicit Postgres type
|
|
73
|
+
if db_type == cls.POSTGRES:
|
|
74
|
+
if not config.database_url:
|
|
75
|
+
raise ValueError("DATABASE_URL must be set when using Postgres backend")
|
|
76
|
+
logger.info(f"Using Postgres database: {config.database_url}")
|
|
77
|
+
return config.database_url
|
|
78
|
+
|
|
79
|
+
# Check if Postgres backend is configured (for backward compatibility)
|
|
80
|
+
if config.database_backend == DatabaseBackend.POSTGRES:
|
|
81
|
+
if not config.database_url:
|
|
82
|
+
raise ValueError("DATABASE_URL must be set when using Postgres backend")
|
|
83
|
+
logger.info(f"Using Postgres database: {config.database_url}")
|
|
84
|
+
return config.database_url
|
|
85
|
+
|
|
86
|
+
# SQLite databases
|
|
38
87
|
if db_type == cls.MEMORY:
|
|
39
88
|
logger.info("Using in-memory SQLite database")
|
|
40
89
|
return "sqlite+aiosqlite://"
|
|
@@ -62,7 +111,14 @@ async def scoped_session(
|
|
|
62
111
|
factory = get_scoped_session_factory(session_maker)
|
|
63
112
|
session = factory()
|
|
64
113
|
try:
|
|
65
|
-
|
|
114
|
+
# Only enable foreign keys for SQLite (Postgres has them enabled by default)
|
|
115
|
+
# Detect database type from session's bind (engine) dialect
|
|
116
|
+
engine = session.get_bind()
|
|
117
|
+
dialect_name = engine.dialect.name
|
|
118
|
+
|
|
119
|
+
if dialect_name == "sqlite":
|
|
120
|
+
await session.execute(text("PRAGMA foreign_keys=ON"))
|
|
121
|
+
|
|
66
122
|
yield session
|
|
67
123
|
await session.commit()
|
|
68
124
|
except Exception:
|
|
@@ -73,21 +129,189 @@ async def scoped_session(
|
|
|
73
129
|
await factory.remove()
|
|
74
130
|
|
|
75
131
|
|
|
132
|
+
def _configure_sqlite_connection(dbapi_conn, enable_wal: bool = True) -> None:
|
|
133
|
+
"""Configure SQLite connection with WAL mode and optimizations.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
dbapi_conn: Database API connection object
|
|
137
|
+
enable_wal: Whether to enable WAL mode (should be False for in-memory databases)
|
|
138
|
+
"""
|
|
139
|
+
cursor = dbapi_conn.cursor()
|
|
140
|
+
try:
|
|
141
|
+
# Enable WAL mode for better concurrency (not supported for in-memory databases)
|
|
142
|
+
if enable_wal:
|
|
143
|
+
cursor.execute("PRAGMA journal_mode=WAL")
|
|
144
|
+
# Set busy timeout to handle locked databases
|
|
145
|
+
cursor.execute("PRAGMA busy_timeout=10000") # 10 seconds
|
|
146
|
+
# Optimize for performance
|
|
147
|
+
cursor.execute("PRAGMA synchronous=NORMAL")
|
|
148
|
+
cursor.execute("PRAGMA cache_size=-64000") # 64MB cache
|
|
149
|
+
cursor.execute("PRAGMA temp_store=MEMORY")
|
|
150
|
+
# Windows-specific optimizations
|
|
151
|
+
if os.name == "nt":
|
|
152
|
+
cursor.execute("PRAGMA locking_mode=NORMAL") # Ensure normal locking on Windows
|
|
153
|
+
except Exception as e:
|
|
154
|
+
# Log but don't fail - some PRAGMAs may not be supported
|
|
155
|
+
logger.warning(f"Failed to configure SQLite connection: {e}")
|
|
156
|
+
finally:
|
|
157
|
+
cursor.close()
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _create_sqlite_engine(db_url: str, db_type: DatabaseType) -> AsyncEngine:
|
|
161
|
+
"""Create SQLite async engine with appropriate configuration.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
db_url: SQLite connection URL
|
|
165
|
+
db_type: Database type (MEMORY or FILESYSTEM)
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
Configured async engine for SQLite
|
|
169
|
+
"""
|
|
170
|
+
# Configure connection args with Windows-specific settings
|
|
171
|
+
connect_args: dict[str, bool | float | None] = {"check_same_thread": False}
|
|
172
|
+
|
|
173
|
+
# Add Windows-specific parameters to improve reliability
|
|
174
|
+
if os.name == "nt": # Windows
|
|
175
|
+
connect_args.update(
|
|
176
|
+
{
|
|
177
|
+
"timeout": 30.0, # Increase timeout to 30 seconds for Windows
|
|
178
|
+
"isolation_level": None, # Use autocommit mode
|
|
179
|
+
}
|
|
180
|
+
)
|
|
181
|
+
# Use NullPool for Windows filesystem databases to avoid connection pooling issues
|
|
182
|
+
# Important: Do NOT use NullPool for in-memory databases as it will destroy the database
|
|
183
|
+
# between connections
|
|
184
|
+
if db_type == DatabaseType.FILESYSTEM:
|
|
185
|
+
engine = create_async_engine(
|
|
186
|
+
db_url,
|
|
187
|
+
connect_args=connect_args,
|
|
188
|
+
poolclass=NullPool, # Disable connection pooling on Windows
|
|
189
|
+
echo=False,
|
|
190
|
+
)
|
|
191
|
+
else:
|
|
192
|
+
# In-memory databases need connection pooling to maintain state
|
|
193
|
+
engine = create_async_engine(db_url, connect_args=connect_args)
|
|
194
|
+
else:
|
|
195
|
+
engine = create_async_engine(db_url, connect_args=connect_args)
|
|
196
|
+
|
|
197
|
+
# Enable WAL mode for better concurrency and reliability
|
|
198
|
+
# Note: WAL mode is not supported for in-memory databases
|
|
199
|
+
enable_wal = db_type != DatabaseType.MEMORY
|
|
200
|
+
|
|
201
|
+
@event.listens_for(engine.sync_engine, "connect")
|
|
202
|
+
def enable_wal_mode(dbapi_conn, connection_record):
|
|
203
|
+
"""Enable WAL mode on each connection."""
|
|
204
|
+
_configure_sqlite_connection(dbapi_conn, enable_wal=enable_wal)
|
|
205
|
+
|
|
206
|
+
return engine
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _create_postgres_engine(db_url: str, config: BasicMemoryConfig) -> AsyncEngine:
|
|
210
|
+
"""Create Postgres async engine with appropriate configuration.
|
|
211
|
+
|
|
212
|
+
Args:
|
|
213
|
+
db_url: Postgres connection URL (postgresql+asyncpg://...)
|
|
214
|
+
config: BasicMemoryConfig with pool settings
|
|
215
|
+
|
|
216
|
+
Returns:
|
|
217
|
+
Configured async engine for Postgres
|
|
218
|
+
"""
|
|
219
|
+
# Use NullPool connection issues.
|
|
220
|
+
# Assume connection pooler like PgBouncer handles connection pooling.
|
|
221
|
+
engine = create_async_engine(
|
|
222
|
+
db_url,
|
|
223
|
+
echo=False,
|
|
224
|
+
poolclass=NullPool, # No pooling - fresh connection per request
|
|
225
|
+
connect_args={
|
|
226
|
+
# Disable statement cache to avoid issues with prepared statements on reconnect
|
|
227
|
+
"statement_cache_size": 0,
|
|
228
|
+
# Allow 30s for commands (Neon cold start can take 2-5s, sometimes longer)
|
|
229
|
+
"command_timeout": 30,
|
|
230
|
+
# Allow 30s for initial connection (Neon wake-up time)
|
|
231
|
+
"timeout": 30,
|
|
232
|
+
"server_settings": {
|
|
233
|
+
"application_name": "basic-memory",
|
|
234
|
+
# Statement timeout for queries (30s to allow for cold start)
|
|
235
|
+
"statement_timeout": "30s",
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
)
|
|
239
|
+
logger.debug("Created Postgres engine with NullPool (no connection pooling)")
|
|
240
|
+
|
|
241
|
+
return engine
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def _create_engine_and_session(
|
|
245
|
+
db_path: Path,
|
|
246
|
+
db_type: DatabaseType = DatabaseType.FILESYSTEM,
|
|
247
|
+
config: Optional[BasicMemoryConfig] = None,
|
|
248
|
+
) -> tuple[AsyncEngine, async_sessionmaker[AsyncSession]]:
|
|
249
|
+
"""Internal helper to create engine and session maker.
|
|
250
|
+
|
|
251
|
+
Args:
|
|
252
|
+
db_path: Path to database file (used for SQLite, ignored for Postgres)
|
|
253
|
+
db_type: Type of database (MEMORY, FILESYSTEM, or POSTGRES)
|
|
254
|
+
config: Optional explicit config. If not provided, reads from ConfigManager.
|
|
255
|
+
Prefer passing explicitly from composition roots.
|
|
256
|
+
|
|
257
|
+
Returns:
|
|
258
|
+
Tuple of (engine, session_maker)
|
|
259
|
+
"""
|
|
260
|
+
# Prefer explicit parameter; fall back to ConfigManager for backwards compatibility
|
|
261
|
+
if config is None:
|
|
262
|
+
config = ConfigManager().config
|
|
263
|
+
db_url = DatabaseType.get_db_url(db_path, db_type, config)
|
|
264
|
+
logger.debug(f"Creating engine for db_url: {db_url}")
|
|
265
|
+
|
|
266
|
+
# Delegate to backend-specific engine creation
|
|
267
|
+
# Check explicit POSTGRES type first, then config setting
|
|
268
|
+
if db_type == DatabaseType.POSTGRES or config.database_backend == DatabaseBackend.POSTGRES:
|
|
269
|
+
engine = _create_postgres_engine(db_url, config)
|
|
270
|
+
else:
|
|
271
|
+
engine = _create_sqlite_engine(db_url, db_type)
|
|
272
|
+
|
|
273
|
+
session_maker = async_sessionmaker(engine, expire_on_commit=False)
|
|
274
|
+
return engine, session_maker
|
|
275
|
+
|
|
276
|
+
|
|
76
277
|
async def get_or_create_db(
|
|
77
278
|
db_path: Path,
|
|
78
279
|
db_type: DatabaseType = DatabaseType.FILESYSTEM,
|
|
280
|
+
ensure_migrations: bool = True,
|
|
281
|
+
config: Optional[BasicMemoryConfig] = None,
|
|
79
282
|
) -> tuple[AsyncEngine, async_sessionmaker[AsyncSession]]: # pragma: no cover
|
|
80
|
-
"""Get or create database engine and session maker.
|
|
283
|
+
"""Get or create database engine and session maker.
|
|
284
|
+
|
|
285
|
+
Args:
|
|
286
|
+
db_path: Path to database file
|
|
287
|
+
db_type: Type of database
|
|
288
|
+
ensure_migrations: Whether to run migrations
|
|
289
|
+
config: Optional explicit config. If not provided, reads from ConfigManager.
|
|
290
|
+
Prefer passing explicitly from composition roots.
|
|
291
|
+
"""
|
|
81
292
|
global _engine, _session_maker
|
|
82
293
|
|
|
294
|
+
# Prefer explicit parameter; fall back to ConfigManager for backwards compatibility
|
|
295
|
+
if config is None:
|
|
296
|
+
config = ConfigManager().config
|
|
297
|
+
|
|
298
|
+
if _engine is None:
|
|
299
|
+
_engine, _session_maker = _create_engine_and_session(db_path, db_type, config)
|
|
300
|
+
|
|
301
|
+
# Run migrations automatically unless explicitly disabled
|
|
302
|
+
if ensure_migrations:
|
|
303
|
+
await run_migrations(config, db_type)
|
|
304
|
+
|
|
305
|
+
# These checks should never fail since we just created the engine and session maker
|
|
306
|
+
# if they were None, but we'll check anyway for the type checker
|
|
83
307
|
if _engine is None:
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
308
|
+
logger.error("Failed to create database engine", db_path=str(db_path))
|
|
309
|
+
raise RuntimeError("Database engine initialization failed")
|
|
310
|
+
|
|
311
|
+
if _session_maker is None:
|
|
312
|
+
logger.error("Failed to create session maker", db_path=str(db_path))
|
|
313
|
+
raise RuntimeError("Session maker initialization failed")
|
|
88
314
|
|
|
89
|
-
assert _engine is not None # for type checker
|
|
90
|
-
assert _session_maker is not None # for type checker
|
|
91
315
|
return _engine, _session_maker
|
|
92
316
|
|
|
93
317
|
|
|
@@ -105,24 +329,34 @@ async def shutdown_db() -> None: # pragma: no cover
|
|
|
105
329
|
async def engine_session_factory(
|
|
106
330
|
db_path: Path,
|
|
107
331
|
db_type: DatabaseType = DatabaseType.MEMORY,
|
|
332
|
+
config: Optional[BasicMemoryConfig] = None,
|
|
108
333
|
) -> AsyncGenerator[tuple[AsyncEngine, async_sessionmaker[AsyncSession]], None]:
|
|
109
334
|
"""Create engine and session factory.
|
|
110
335
|
|
|
111
336
|
Note: This is primarily used for testing where we want a fresh database
|
|
112
337
|
for each test. For production use, use get_or_create_db() instead.
|
|
338
|
+
|
|
339
|
+
Args:
|
|
340
|
+
db_path: Path to database file
|
|
341
|
+
db_type: Type of database
|
|
342
|
+
config: Optional explicit config. If not provided, reads from ConfigManager.
|
|
113
343
|
"""
|
|
114
344
|
|
|
115
345
|
global _engine, _session_maker
|
|
116
346
|
|
|
117
|
-
|
|
118
|
-
|
|
347
|
+
# Use the same helper function as production code
|
|
348
|
+
_engine, _session_maker = _create_engine_and_session(db_path, db_type, config)
|
|
119
349
|
|
|
120
|
-
_engine = create_async_engine(db_url, connect_args={"check_same_thread": False})
|
|
121
350
|
try:
|
|
122
|
-
|
|
351
|
+
# Verify that engine and session maker are initialized
|
|
352
|
+
if _engine is None: # pragma: no cover
|
|
353
|
+
logger.error("Database engine is None in engine_session_factory")
|
|
354
|
+
raise RuntimeError("Database engine initialization failed")
|
|
355
|
+
|
|
356
|
+
if _session_maker is None: # pragma: no cover
|
|
357
|
+
logger.error("Session maker is None in engine_session_factory")
|
|
358
|
+
raise RuntimeError("Session maker initialization failed")
|
|
123
359
|
|
|
124
|
-
assert _engine is not None # for type checker
|
|
125
|
-
assert _session_maker is not None # for type checker
|
|
126
360
|
yield _engine, _session_maker
|
|
127
361
|
finally:
|
|
128
362
|
if _engine:
|
|
@@ -131,8 +365,14 @@ async def engine_session_factory(
|
|
|
131
365
|
_session_maker = None
|
|
132
366
|
|
|
133
367
|
|
|
134
|
-
async def run_migrations(
|
|
135
|
-
|
|
368
|
+
async def run_migrations(
|
|
369
|
+
app_config: BasicMemoryConfig, database_type=DatabaseType.FILESYSTEM
|
|
370
|
+
): # pragma: no cover
|
|
371
|
+
"""Run any pending alembic migrations.
|
|
372
|
+
|
|
373
|
+
Note: Alembic tracks which migrations have been applied via the alembic_version table,
|
|
374
|
+
so it's safe to call this multiple times - it will only run pending migrations.
|
|
375
|
+
"""
|
|
136
376
|
logger.info("Running database migrations...")
|
|
137
377
|
try:
|
|
138
378
|
# Get the absolute path to the alembic directory relative to this file
|
|
@@ -147,15 +387,32 @@ async def run_migrations(app_config: ProjectConfig, database_type=DatabaseType.F
|
|
|
147
387
|
)
|
|
148
388
|
config.set_main_option("timezone", "UTC")
|
|
149
389
|
config.set_main_option("revision_environment", "false")
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
390
|
+
|
|
391
|
+
# Get the correct database URL based on backend configuration
|
|
392
|
+
# No URL conversion needed - env.py now handles both async and sync engines
|
|
393
|
+
db_url = DatabaseType.get_db_url(app_config.database_path, database_type, app_config)
|
|
394
|
+
config.set_main_option("sqlalchemy.url", db_url)
|
|
153
395
|
|
|
154
396
|
command.upgrade(config, "head")
|
|
155
397
|
logger.info("Migrations completed successfully")
|
|
156
398
|
|
|
157
|
-
|
|
158
|
-
|
|
399
|
+
# Get session maker - ensure we don't trigger recursive migration calls
|
|
400
|
+
if _session_maker is None:
|
|
401
|
+
_, session_maker = _create_engine_and_session(app_config.database_path, database_type)
|
|
402
|
+
else:
|
|
403
|
+
session_maker = _session_maker
|
|
404
|
+
|
|
405
|
+
# Initialize the search index schema
|
|
406
|
+
# For SQLite: Create FTS5 virtual table
|
|
407
|
+
# For Postgres: No-op (tsvector column added by migrations)
|
|
408
|
+
# The project_id is not used for init_search_index, so we pass a dummy value
|
|
409
|
+
if (
|
|
410
|
+
database_type == DatabaseType.POSTGRES
|
|
411
|
+
or app_config.database_backend == DatabaseBackend.POSTGRES
|
|
412
|
+
):
|
|
413
|
+
await PostgresSearchRepository(session_maker, 1).init_search_index()
|
|
414
|
+
else:
|
|
415
|
+
await SQLiteSearchRepository(session_maker, 1).init_search_index()
|
|
159
416
|
except Exception as e: # pragma: no cover
|
|
160
417
|
logger.error(f"Error running migrations: {e}")
|
|
161
418
|
raise
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
"""Dependency injection for basic-memory.
|
|
2
|
+
|
|
3
|
+
This package provides FastAPI dependencies organized by feature:
|
|
4
|
+
- config: Application configuration
|
|
5
|
+
- db: Database/session management
|
|
6
|
+
- projects: Project resolution and config
|
|
7
|
+
- repositories: Data access layer
|
|
8
|
+
- services: Business logic layer
|
|
9
|
+
- importers: Import functionality
|
|
10
|
+
|
|
11
|
+
For backwards compatibility, all dependencies are re-exported from this module.
|
|
12
|
+
New code should import from specific submodules to reduce coupling.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
# Re-export everything for backwards compatibility
|
|
16
|
+
# Eventually, callers should import from specific submodules
|
|
17
|
+
|
|
18
|
+
from basic_memory.deps.config import (
|
|
19
|
+
get_app_config,
|
|
20
|
+
AppConfigDep,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
from basic_memory.deps.db import (
|
|
24
|
+
get_engine_factory,
|
|
25
|
+
EngineFactoryDep,
|
|
26
|
+
get_session_maker,
|
|
27
|
+
SessionMakerDep,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
from basic_memory.deps.projects import (
|
|
31
|
+
get_project_repository,
|
|
32
|
+
ProjectRepositoryDep,
|
|
33
|
+
ProjectPathDep,
|
|
34
|
+
get_project_id,
|
|
35
|
+
ProjectIdDep,
|
|
36
|
+
get_project_config,
|
|
37
|
+
ProjectConfigDep,
|
|
38
|
+
validate_project_id,
|
|
39
|
+
ProjectIdPathDep,
|
|
40
|
+
get_project_config_v2,
|
|
41
|
+
ProjectConfigV2Dep,
|
|
42
|
+
validate_project_external_id,
|
|
43
|
+
ProjectExternalIdPathDep,
|
|
44
|
+
get_project_config_v2_external,
|
|
45
|
+
ProjectConfigV2ExternalDep,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
from basic_memory.deps.repositories import (
|
|
49
|
+
get_entity_repository,
|
|
50
|
+
EntityRepositoryDep,
|
|
51
|
+
get_entity_repository_v2,
|
|
52
|
+
EntityRepositoryV2Dep,
|
|
53
|
+
get_entity_repository_v2_external,
|
|
54
|
+
EntityRepositoryV2ExternalDep,
|
|
55
|
+
get_observation_repository,
|
|
56
|
+
ObservationRepositoryDep,
|
|
57
|
+
get_observation_repository_v2,
|
|
58
|
+
ObservationRepositoryV2Dep,
|
|
59
|
+
get_observation_repository_v2_external,
|
|
60
|
+
ObservationRepositoryV2ExternalDep,
|
|
61
|
+
get_relation_repository,
|
|
62
|
+
RelationRepositoryDep,
|
|
63
|
+
get_relation_repository_v2,
|
|
64
|
+
RelationRepositoryV2Dep,
|
|
65
|
+
get_relation_repository_v2_external,
|
|
66
|
+
RelationRepositoryV2ExternalDep,
|
|
67
|
+
get_search_repository,
|
|
68
|
+
SearchRepositoryDep,
|
|
69
|
+
get_search_repository_v2,
|
|
70
|
+
SearchRepositoryV2Dep,
|
|
71
|
+
get_search_repository_v2_external,
|
|
72
|
+
SearchRepositoryV2ExternalDep,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
from basic_memory.deps.services import (
|
|
76
|
+
get_entity_parser,
|
|
77
|
+
EntityParserDep,
|
|
78
|
+
get_entity_parser_v2,
|
|
79
|
+
EntityParserV2Dep,
|
|
80
|
+
get_entity_parser_v2_external,
|
|
81
|
+
EntityParserV2ExternalDep,
|
|
82
|
+
get_markdown_processor,
|
|
83
|
+
MarkdownProcessorDep,
|
|
84
|
+
get_markdown_processor_v2,
|
|
85
|
+
MarkdownProcessorV2Dep,
|
|
86
|
+
get_markdown_processor_v2_external,
|
|
87
|
+
MarkdownProcessorV2ExternalDep,
|
|
88
|
+
get_file_service,
|
|
89
|
+
FileServiceDep,
|
|
90
|
+
get_file_service_v2,
|
|
91
|
+
FileServiceV2Dep,
|
|
92
|
+
get_file_service_v2_external,
|
|
93
|
+
FileServiceV2ExternalDep,
|
|
94
|
+
get_search_service,
|
|
95
|
+
SearchServiceDep,
|
|
96
|
+
get_search_service_v2,
|
|
97
|
+
SearchServiceV2Dep,
|
|
98
|
+
get_search_service_v2_external,
|
|
99
|
+
SearchServiceV2ExternalDep,
|
|
100
|
+
get_link_resolver,
|
|
101
|
+
LinkResolverDep,
|
|
102
|
+
get_link_resolver_v2,
|
|
103
|
+
LinkResolverV2Dep,
|
|
104
|
+
get_link_resolver_v2_external,
|
|
105
|
+
LinkResolverV2ExternalDep,
|
|
106
|
+
get_entity_service,
|
|
107
|
+
EntityServiceDep,
|
|
108
|
+
get_entity_service_v2,
|
|
109
|
+
EntityServiceV2Dep,
|
|
110
|
+
get_entity_service_v2_external,
|
|
111
|
+
EntityServiceV2ExternalDep,
|
|
112
|
+
get_context_service,
|
|
113
|
+
ContextServiceDep,
|
|
114
|
+
get_context_service_v2,
|
|
115
|
+
ContextServiceV2Dep,
|
|
116
|
+
get_context_service_v2_external,
|
|
117
|
+
ContextServiceV2ExternalDep,
|
|
118
|
+
get_sync_service,
|
|
119
|
+
SyncServiceDep,
|
|
120
|
+
get_sync_service_v2,
|
|
121
|
+
SyncServiceV2Dep,
|
|
122
|
+
get_sync_service_v2_external,
|
|
123
|
+
SyncServiceV2ExternalDep,
|
|
124
|
+
get_project_service,
|
|
125
|
+
ProjectServiceDep,
|
|
126
|
+
get_directory_service,
|
|
127
|
+
DirectoryServiceDep,
|
|
128
|
+
get_directory_service_v2,
|
|
129
|
+
DirectoryServiceV2Dep,
|
|
130
|
+
get_directory_service_v2_external,
|
|
131
|
+
DirectoryServiceV2ExternalDep,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
from basic_memory.deps.importers import (
|
|
135
|
+
get_chatgpt_importer,
|
|
136
|
+
ChatGPTImporterDep,
|
|
137
|
+
get_chatgpt_importer_v2,
|
|
138
|
+
ChatGPTImporterV2Dep,
|
|
139
|
+
get_chatgpt_importer_v2_external,
|
|
140
|
+
ChatGPTImporterV2ExternalDep,
|
|
141
|
+
get_claude_conversations_importer,
|
|
142
|
+
ClaudeConversationsImporterDep,
|
|
143
|
+
get_claude_conversations_importer_v2,
|
|
144
|
+
ClaudeConversationsImporterV2Dep,
|
|
145
|
+
get_claude_conversations_importer_v2_external,
|
|
146
|
+
ClaudeConversationsImporterV2ExternalDep,
|
|
147
|
+
get_claude_projects_importer,
|
|
148
|
+
ClaudeProjectsImporterDep,
|
|
149
|
+
get_claude_projects_importer_v2,
|
|
150
|
+
ClaudeProjectsImporterV2Dep,
|
|
151
|
+
get_claude_projects_importer_v2_external,
|
|
152
|
+
ClaudeProjectsImporterV2ExternalDep,
|
|
153
|
+
get_memory_json_importer,
|
|
154
|
+
MemoryJsonImporterDep,
|
|
155
|
+
get_memory_json_importer_v2,
|
|
156
|
+
MemoryJsonImporterV2Dep,
|
|
157
|
+
get_memory_json_importer_v2_external,
|
|
158
|
+
MemoryJsonImporterV2ExternalDep,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
__all__ = [
|
|
162
|
+
# Config
|
|
163
|
+
"get_app_config",
|
|
164
|
+
"AppConfigDep",
|
|
165
|
+
# Database
|
|
166
|
+
"get_engine_factory",
|
|
167
|
+
"EngineFactoryDep",
|
|
168
|
+
"get_session_maker",
|
|
169
|
+
"SessionMakerDep",
|
|
170
|
+
# Projects
|
|
171
|
+
"get_project_repository",
|
|
172
|
+
"ProjectRepositoryDep",
|
|
173
|
+
"ProjectPathDep",
|
|
174
|
+
"get_project_id",
|
|
175
|
+
"ProjectIdDep",
|
|
176
|
+
"get_project_config",
|
|
177
|
+
"ProjectConfigDep",
|
|
178
|
+
"validate_project_id",
|
|
179
|
+
"ProjectIdPathDep",
|
|
180
|
+
"get_project_config_v2",
|
|
181
|
+
"ProjectConfigV2Dep",
|
|
182
|
+
"validate_project_external_id",
|
|
183
|
+
"ProjectExternalIdPathDep",
|
|
184
|
+
"get_project_config_v2_external",
|
|
185
|
+
"ProjectConfigV2ExternalDep",
|
|
186
|
+
# Repositories
|
|
187
|
+
"get_entity_repository",
|
|
188
|
+
"EntityRepositoryDep",
|
|
189
|
+
"get_entity_repository_v2",
|
|
190
|
+
"EntityRepositoryV2Dep",
|
|
191
|
+
"get_entity_repository_v2_external",
|
|
192
|
+
"EntityRepositoryV2ExternalDep",
|
|
193
|
+
"get_observation_repository",
|
|
194
|
+
"ObservationRepositoryDep",
|
|
195
|
+
"get_observation_repository_v2",
|
|
196
|
+
"ObservationRepositoryV2Dep",
|
|
197
|
+
"get_observation_repository_v2_external",
|
|
198
|
+
"ObservationRepositoryV2ExternalDep",
|
|
199
|
+
"get_relation_repository",
|
|
200
|
+
"RelationRepositoryDep",
|
|
201
|
+
"get_relation_repository_v2",
|
|
202
|
+
"RelationRepositoryV2Dep",
|
|
203
|
+
"get_relation_repository_v2_external",
|
|
204
|
+
"RelationRepositoryV2ExternalDep",
|
|
205
|
+
"get_search_repository",
|
|
206
|
+
"SearchRepositoryDep",
|
|
207
|
+
"get_search_repository_v2",
|
|
208
|
+
"SearchRepositoryV2Dep",
|
|
209
|
+
"get_search_repository_v2_external",
|
|
210
|
+
"SearchRepositoryV2ExternalDep",
|
|
211
|
+
# Services
|
|
212
|
+
"get_entity_parser",
|
|
213
|
+
"EntityParserDep",
|
|
214
|
+
"get_entity_parser_v2",
|
|
215
|
+
"EntityParserV2Dep",
|
|
216
|
+
"get_entity_parser_v2_external",
|
|
217
|
+
"EntityParserV2ExternalDep",
|
|
218
|
+
"get_markdown_processor",
|
|
219
|
+
"MarkdownProcessorDep",
|
|
220
|
+
"get_markdown_processor_v2",
|
|
221
|
+
"MarkdownProcessorV2Dep",
|
|
222
|
+
"get_markdown_processor_v2_external",
|
|
223
|
+
"MarkdownProcessorV2ExternalDep",
|
|
224
|
+
"get_file_service",
|
|
225
|
+
"FileServiceDep",
|
|
226
|
+
"get_file_service_v2",
|
|
227
|
+
"FileServiceV2Dep",
|
|
228
|
+
"get_file_service_v2_external",
|
|
229
|
+
"FileServiceV2ExternalDep",
|
|
230
|
+
"get_search_service",
|
|
231
|
+
"SearchServiceDep",
|
|
232
|
+
"get_search_service_v2",
|
|
233
|
+
"SearchServiceV2Dep",
|
|
234
|
+
"get_search_service_v2_external",
|
|
235
|
+
"SearchServiceV2ExternalDep",
|
|
236
|
+
"get_link_resolver",
|
|
237
|
+
"LinkResolverDep",
|
|
238
|
+
"get_link_resolver_v2",
|
|
239
|
+
"LinkResolverV2Dep",
|
|
240
|
+
"get_link_resolver_v2_external",
|
|
241
|
+
"LinkResolverV2ExternalDep",
|
|
242
|
+
"get_entity_service",
|
|
243
|
+
"EntityServiceDep",
|
|
244
|
+
"get_entity_service_v2",
|
|
245
|
+
"EntityServiceV2Dep",
|
|
246
|
+
"get_entity_service_v2_external",
|
|
247
|
+
"EntityServiceV2ExternalDep",
|
|
248
|
+
"get_context_service",
|
|
249
|
+
"ContextServiceDep",
|
|
250
|
+
"get_context_service_v2",
|
|
251
|
+
"ContextServiceV2Dep",
|
|
252
|
+
"get_context_service_v2_external",
|
|
253
|
+
"ContextServiceV2ExternalDep",
|
|
254
|
+
"get_sync_service",
|
|
255
|
+
"SyncServiceDep",
|
|
256
|
+
"get_sync_service_v2",
|
|
257
|
+
"SyncServiceV2Dep",
|
|
258
|
+
"get_sync_service_v2_external",
|
|
259
|
+
"SyncServiceV2ExternalDep",
|
|
260
|
+
"get_project_service",
|
|
261
|
+
"ProjectServiceDep",
|
|
262
|
+
"get_directory_service",
|
|
263
|
+
"DirectoryServiceDep",
|
|
264
|
+
"get_directory_service_v2",
|
|
265
|
+
"DirectoryServiceV2Dep",
|
|
266
|
+
"get_directory_service_v2_external",
|
|
267
|
+
"DirectoryServiceV2ExternalDep",
|
|
268
|
+
# Importers
|
|
269
|
+
"get_chatgpt_importer",
|
|
270
|
+
"ChatGPTImporterDep",
|
|
271
|
+
"get_chatgpt_importer_v2",
|
|
272
|
+
"ChatGPTImporterV2Dep",
|
|
273
|
+
"get_chatgpt_importer_v2_external",
|
|
274
|
+
"ChatGPTImporterV2ExternalDep",
|
|
275
|
+
"get_claude_conversations_importer",
|
|
276
|
+
"ClaudeConversationsImporterDep",
|
|
277
|
+
"get_claude_conversations_importer_v2",
|
|
278
|
+
"ClaudeConversationsImporterV2Dep",
|
|
279
|
+
"get_claude_conversations_importer_v2_external",
|
|
280
|
+
"ClaudeConversationsImporterV2ExternalDep",
|
|
281
|
+
"get_claude_projects_importer",
|
|
282
|
+
"ClaudeProjectsImporterDep",
|
|
283
|
+
"get_claude_projects_importer_v2",
|
|
284
|
+
"ClaudeProjectsImporterV2Dep",
|
|
285
|
+
"get_claude_projects_importer_v2_external",
|
|
286
|
+
"ClaudeProjectsImporterV2ExternalDep",
|
|
287
|
+
"get_memory_json_importer",
|
|
288
|
+
"MemoryJsonImporterDep",
|
|
289
|
+
"get_memory_json_importer_v2",
|
|
290
|
+
"MemoryJsonImporterV2Dep",
|
|
291
|
+
"get_memory_json_importer_v2_external",
|
|
292
|
+
"MemoryJsonImporterV2ExternalDep",
|
|
293
|
+
]
|