basic-memory 0.17.1__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.
- basic_memory/__init__.py +7 -0
- basic_memory/alembic/alembic.ini +119 -0
- basic_memory/alembic/env.py +185 -0
- basic_memory/alembic/migrations.py +24 -0
- basic_memory/alembic/script.py.mako +26 -0
- basic_memory/alembic/versions/314f1ea54dc4_add_postgres_full_text_search_support_.py +131 -0
- basic_memory/alembic/versions/3dae7c7b1564_initial_schema.py +93 -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/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/api/__init__.py +5 -0
- basic_memory/api/app.py +131 -0
- basic_memory/api/routers/__init__.py +11 -0
- 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 +318 -0
- basic_memory/api/routers/management_router.py +80 -0
- basic_memory/api/routers/memory_router.py +90 -0
- basic_memory/api/routers/project_router.py +448 -0
- basic_memory/api/routers/prompt_router.py +260 -0
- basic_memory/api/routers/resource_router.py +249 -0
- basic_memory/api/routers/search_router.py +36 -0
- 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 +182 -0
- basic_memory/api/v2/routers/knowledge_router.py +413 -0
- basic_memory/api/v2/routers/memory_router.py +130 -0
- basic_memory/api/v2/routers/project_router.py +342 -0
- basic_memory/api/v2/routers/prompt_router.py +270 -0
- basic_memory/api/v2/routers/resource_router.py +286 -0
- basic_memory/api/v2/routers/search_router.py +73 -0
- basic_memory/cli/__init__.py +1 -0
- basic_memory/cli/app.py +84 -0
- basic_memory/cli/auth.py +277 -0
- basic_memory/cli/commands/__init__.py +18 -0
- basic_memory/cli/commands/cloud/__init__.py +6 -0
- basic_memory/cli/commands/cloud/api_client.py +112 -0
- basic_memory/cli/commands/cloud/bisync_commands.py +110 -0
- basic_memory/cli/commands/cloud/cloud_utils.py +101 -0
- basic_memory/cli/commands/cloud/core_commands.py +195 -0
- basic_memory/cli/commands/cloud/rclone_commands.py +371 -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 +233 -0
- basic_memory/cli/commands/cloud/upload_command.py +124 -0
- basic_memory/cli/commands/command_utils.py +77 -0
- basic_memory/cli/commands/db.py +44 -0
- basic_memory/cli/commands/format.py +198 -0
- basic_memory/cli/commands/import_chatgpt.py +84 -0
- basic_memory/cli/commands/import_claude_conversations.py +87 -0
- basic_memory/cli/commands/import_claude_projects.py +86 -0
- basic_memory/cli/commands/import_memory_json.py +87 -0
- basic_memory/cli/commands/mcp.py +76 -0
- basic_memory/cli/commands/project.py +889 -0
- basic_memory/cli/commands/status.py +174 -0
- basic_memory/cli/commands/telemetry.py +81 -0
- basic_memory/cli/commands/tool.py +341 -0
- basic_memory/cli/main.py +28 -0
- basic_memory/config.py +616 -0
- basic_memory/db.py +394 -0
- basic_memory/deps.py +705 -0
- basic_memory/file_utils.py +478 -0
- basic_memory/ignore_utils.py +297 -0
- basic_memory/importers/__init__.py +27 -0
- basic_memory/importers/base.py +79 -0
- basic_memory/importers/chatgpt_importer.py +232 -0
- basic_memory/importers/claude_conversations_importer.py +180 -0
- basic_memory/importers/claude_projects_importer.py +148 -0
- basic_memory/importers/memory_json_importer.py +108 -0
- basic_memory/importers/utils.py +61 -0
- basic_memory/markdown/__init__.py +21 -0
- basic_memory/markdown/entity_parser.py +279 -0
- basic_memory/markdown/markdown_processor.py +160 -0
- basic_memory/markdown/plugins.py +242 -0
- basic_memory/markdown/schemas.py +70 -0
- basic_memory/markdown/utils.py +117 -0
- basic_memory/mcp/__init__.py +1 -0
- basic_memory/mcp/async_client.py +139 -0
- basic_memory/mcp/project_context.py +141 -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 +81 -0
- basic_memory/mcp/tools/__init__.py +48 -0
- 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 +242 -0
- basic_memory/mcp/tools/edit_note.py +324 -0
- basic_memory/mcp/tools/list_directory.py +168 -0
- basic_memory/mcp/tools/move_note.py +551 -0
- basic_memory/mcp/tools/project_management.py +201 -0
- basic_memory/mcp/tools/read_content.py +281 -0
- basic_memory/mcp/tools/read_note.py +267 -0
- basic_memory/mcp/tools/recent_activity.py +534 -0
- basic_memory/mcp/tools/search.py +385 -0
- basic_memory/mcp/tools/utils.py +540 -0
- basic_memory/mcp/tools/view_note.py +78 -0
- basic_memory/mcp/tools/write_note.py +230 -0
- basic_memory/models/__init__.py +15 -0
- basic_memory/models/base.py +10 -0
- basic_memory/models/knowledge.py +226 -0
- basic_memory/models/project.py +87 -0
- basic_memory/models/search.py +85 -0
- basic_memory/repository/__init__.py +11 -0
- basic_memory/repository/entity_repository.py +503 -0
- basic_memory/repository/observation_repository.py +73 -0
- basic_memory/repository/postgres_search_repository.py +379 -0
- basic_memory/repository/project_info_repository.py +10 -0
- basic_memory/repository/project_repository.py +128 -0
- basic_memory/repository/relation_repository.py +146 -0
- basic_memory/repository/repository.py +385 -0
- basic_memory/repository/search_index_row.py +95 -0
- basic_memory/repository/search_repository.py +94 -0
- basic_memory/repository/search_repository_base.py +241 -0
- basic_memory/repository/sqlite_search_repository.py +439 -0
- basic_memory/schemas/__init__.py +86 -0
- basic_memory/schemas/base.py +297 -0
- basic_memory/schemas/cloud.py +50 -0
- basic_memory/schemas/delete.py +37 -0
- basic_memory/schemas/directory.py +30 -0
- basic_memory/schemas/importer.py +35 -0
- basic_memory/schemas/memory.py +285 -0
- basic_memory/schemas/project_info.py +212 -0
- basic_memory/schemas/prompt.py +90 -0
- basic_memory/schemas/request.py +112 -0
- basic_memory/schemas/response.py +229 -0
- basic_memory/schemas/search.py +117 -0
- basic_memory/schemas/sync_report.py +72 -0
- basic_memory/schemas/v2/__init__.py +27 -0
- basic_memory/schemas/v2/entity.py +129 -0
- basic_memory/schemas/v2/resource.py +46 -0
- basic_memory/services/__init__.py +8 -0
- basic_memory/services/context_service.py +601 -0
- basic_memory/services/directory_service.py +308 -0
- basic_memory/services/entity_service.py +864 -0
- basic_memory/services/exceptions.py +37 -0
- basic_memory/services/file_service.py +541 -0
- basic_memory/services/initialization.py +216 -0
- basic_memory/services/link_resolver.py +121 -0
- basic_memory/services/project_service.py +880 -0
- basic_memory/services/search_service.py +404 -0
- basic_memory/services/service.py +15 -0
- basic_memory/sync/__init__.py +6 -0
- basic_memory/sync/background_sync.py +26 -0
- basic_memory/sync/sync_service.py +1259 -0
- basic_memory/sync/watch_service.py +510 -0
- 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 +468 -0
- basic_memory-0.17.1.dist-info/METADATA +617 -0
- basic_memory-0.17.1.dist-info/RECORD +171 -0
- basic_memory-0.17.1.dist-info/WHEEL +4 -0
- basic_memory-0.17.1.dist-info/entry_points.txt +3 -0
- basic_memory-0.17.1.dist-info/licenses/LICENSE +661 -0
basic_memory/deps.py
ADDED
|
@@ -0,0 +1,705 @@
|
|
|
1
|
+
"""Dependency injection functions for basic-memory services."""
|
|
2
|
+
|
|
3
|
+
from typing import Annotated
|
|
4
|
+
from loguru import logger
|
|
5
|
+
|
|
6
|
+
from fastapi import Depends, HTTPException, Path, status, Request
|
|
7
|
+
from sqlalchemy.ext.asyncio import (
|
|
8
|
+
AsyncSession,
|
|
9
|
+
AsyncEngine,
|
|
10
|
+
async_sessionmaker,
|
|
11
|
+
)
|
|
12
|
+
import pathlib
|
|
13
|
+
|
|
14
|
+
from basic_memory import db
|
|
15
|
+
from basic_memory.config import ProjectConfig, BasicMemoryConfig, ConfigManager
|
|
16
|
+
from basic_memory.importers import (
|
|
17
|
+
ChatGPTImporter,
|
|
18
|
+
ClaudeConversationsImporter,
|
|
19
|
+
ClaudeProjectsImporter,
|
|
20
|
+
MemoryJsonImporter,
|
|
21
|
+
)
|
|
22
|
+
from basic_memory.markdown import EntityParser
|
|
23
|
+
from basic_memory.markdown.markdown_processor import MarkdownProcessor
|
|
24
|
+
from basic_memory.repository.entity_repository import EntityRepository
|
|
25
|
+
from basic_memory.repository.observation_repository import ObservationRepository
|
|
26
|
+
from basic_memory.repository.project_repository import ProjectRepository
|
|
27
|
+
from basic_memory.repository.relation_repository import RelationRepository
|
|
28
|
+
from basic_memory.repository.search_repository import SearchRepository, create_search_repository
|
|
29
|
+
from basic_memory.services import EntityService, ProjectService
|
|
30
|
+
from basic_memory.services.context_service import ContextService
|
|
31
|
+
from basic_memory.services.directory_service import DirectoryService
|
|
32
|
+
from basic_memory.services.file_service import FileService
|
|
33
|
+
from basic_memory.services.link_resolver import LinkResolver
|
|
34
|
+
from basic_memory.services.search_service import SearchService
|
|
35
|
+
from basic_memory.sync import SyncService
|
|
36
|
+
from basic_memory.utils import generate_permalink
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def get_app_config() -> BasicMemoryConfig: # pragma: no cover
|
|
40
|
+
app_config = ConfigManager().config
|
|
41
|
+
return app_config
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
AppConfigDep = Annotated[BasicMemoryConfig, Depends(get_app_config)] # pragma: no cover
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
## project
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
async def get_project_config(
|
|
51
|
+
project: "ProjectPathDep", project_repository: "ProjectRepositoryDep"
|
|
52
|
+
) -> ProjectConfig: # pragma: no cover
|
|
53
|
+
"""Get the current project referenced from request state.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
request: The current request object
|
|
57
|
+
project_repository: Repository for project operations
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
The resolved project config
|
|
61
|
+
|
|
62
|
+
Raises:
|
|
63
|
+
HTTPException: If project is not found
|
|
64
|
+
"""
|
|
65
|
+
# Convert project name to permalink for lookup
|
|
66
|
+
project_permalink = generate_permalink(str(project))
|
|
67
|
+
project_obj = await project_repository.get_by_permalink(project_permalink)
|
|
68
|
+
if project_obj:
|
|
69
|
+
return ProjectConfig(name=project_obj.name, home=pathlib.Path(project_obj.path))
|
|
70
|
+
|
|
71
|
+
# Not found
|
|
72
|
+
raise HTTPException( # pragma: no cover
|
|
73
|
+
status_code=status.HTTP_404_NOT_FOUND, detail=f"Project '{project}' not found."
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
ProjectConfigDep = Annotated[ProjectConfig, Depends(get_project_config)] # pragma: no cover
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
async def get_project_config_v2(
|
|
81
|
+
project_id: "ProjectIdPathDep", project_repository: "ProjectRepositoryDep"
|
|
82
|
+
) -> ProjectConfig: # pragma: no cover
|
|
83
|
+
"""Get the project config for v2 API (uses integer project_id from path).
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
project_id: The validated numeric project ID from the URL path
|
|
87
|
+
project_repository: Repository for project operations
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
The resolved project config
|
|
91
|
+
|
|
92
|
+
Raises:
|
|
93
|
+
HTTPException: If project is not found
|
|
94
|
+
"""
|
|
95
|
+
project_obj = await project_repository.get_by_id(project_id)
|
|
96
|
+
if project_obj:
|
|
97
|
+
return ProjectConfig(name=project_obj.name, home=pathlib.Path(project_obj.path))
|
|
98
|
+
|
|
99
|
+
# Not found (this should not happen since ProjectIdPathDep already validates existence)
|
|
100
|
+
raise HTTPException( # pragma: no cover
|
|
101
|
+
status_code=status.HTTP_404_NOT_FOUND, detail=f"Project with ID {project_id} not found."
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
ProjectConfigV2Dep = Annotated[ProjectConfig, Depends(get_project_config_v2)] # pragma: no cover
|
|
106
|
+
|
|
107
|
+
## sqlalchemy
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
async def get_engine_factory(
|
|
111
|
+
request: Request,
|
|
112
|
+
) -> tuple[AsyncEngine, async_sessionmaker[AsyncSession]]: # pragma: no cover
|
|
113
|
+
"""Get cached engine and session maker from app state.
|
|
114
|
+
|
|
115
|
+
For API requests, returns cached connections from app.state for optimal performance.
|
|
116
|
+
For non-API contexts (CLI), falls back to direct database connection.
|
|
117
|
+
"""
|
|
118
|
+
# Try to get cached connections from app state (API context)
|
|
119
|
+
if (
|
|
120
|
+
hasattr(request, "app")
|
|
121
|
+
and hasattr(request.app.state, "engine")
|
|
122
|
+
and hasattr(request.app.state, "session_maker")
|
|
123
|
+
):
|
|
124
|
+
return request.app.state.engine, request.app.state.session_maker
|
|
125
|
+
|
|
126
|
+
# Fallback for non-API contexts (CLI)
|
|
127
|
+
logger.debug("Using fallback database connection for non-API context")
|
|
128
|
+
app_config = get_app_config()
|
|
129
|
+
engine, session_maker = await db.get_or_create_db(app_config.database_path)
|
|
130
|
+
return engine, session_maker
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
EngineFactoryDep = Annotated[
|
|
134
|
+
tuple[AsyncEngine, async_sessionmaker[AsyncSession]], Depends(get_engine_factory)
|
|
135
|
+
]
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
async def get_session_maker(engine_factory: EngineFactoryDep) -> async_sessionmaker[AsyncSession]:
|
|
139
|
+
"""Get session maker."""
|
|
140
|
+
_, session_maker = engine_factory
|
|
141
|
+
return session_maker
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
SessionMakerDep = Annotated[async_sessionmaker, Depends(get_session_maker)]
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
## repositories
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
async def get_project_repository(
|
|
151
|
+
session_maker: SessionMakerDep,
|
|
152
|
+
) -> ProjectRepository:
|
|
153
|
+
"""Get the project repository."""
|
|
154
|
+
return ProjectRepository(session_maker)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
ProjectRepositoryDep = Annotated[ProjectRepository, Depends(get_project_repository)]
|
|
158
|
+
ProjectPathDep = Annotated[str, Path()] # Use Path dependency to extract from URL
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
async def validate_project_id(
|
|
162
|
+
project_id: int,
|
|
163
|
+
project_repository: ProjectRepositoryDep,
|
|
164
|
+
) -> int:
|
|
165
|
+
"""Validate that a numeric project ID exists in the database.
|
|
166
|
+
|
|
167
|
+
This is used for v2 API endpoints that take project IDs as integers in the path.
|
|
168
|
+
The project_id parameter will be automatically extracted from the URL path by FastAPI.
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
project_id: The numeric project ID from the URL path
|
|
172
|
+
project_repository: Repository for project operations
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
The validated project ID
|
|
176
|
+
|
|
177
|
+
Raises:
|
|
178
|
+
HTTPException: If project with that ID is not found
|
|
179
|
+
"""
|
|
180
|
+
project_obj = await project_repository.get_by_id(project_id)
|
|
181
|
+
if not project_obj:
|
|
182
|
+
raise HTTPException(
|
|
183
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
184
|
+
detail=f"Project with ID {project_id} not found.",
|
|
185
|
+
)
|
|
186
|
+
return project_id
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
# V2 API: Validated integer project ID from path
|
|
190
|
+
ProjectIdPathDep = Annotated[int, Depends(validate_project_id)]
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
async def get_project_id(
|
|
194
|
+
project_repository: ProjectRepositoryDep,
|
|
195
|
+
project: ProjectPathDep,
|
|
196
|
+
) -> int:
|
|
197
|
+
"""Get the current project ID from request state.
|
|
198
|
+
|
|
199
|
+
When using sub-applications with /{project} mounting, the project value
|
|
200
|
+
is stored in request.state by middleware.
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
request: The current request object
|
|
204
|
+
project_repository: Repository for project operations
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
The resolved project ID
|
|
208
|
+
|
|
209
|
+
Raises:
|
|
210
|
+
HTTPException: If project is not found
|
|
211
|
+
"""
|
|
212
|
+
# Convert project name to permalink for lookup
|
|
213
|
+
project_permalink = generate_permalink(str(project))
|
|
214
|
+
project_obj = await project_repository.get_by_permalink(project_permalink)
|
|
215
|
+
if project_obj:
|
|
216
|
+
return project_obj.id
|
|
217
|
+
|
|
218
|
+
# Try by name if permalink lookup fails
|
|
219
|
+
project_obj = await project_repository.get_by_name(str(project)) # pragma: no cover
|
|
220
|
+
if project_obj: # pragma: no cover
|
|
221
|
+
return project_obj.id
|
|
222
|
+
|
|
223
|
+
# Not found
|
|
224
|
+
raise HTTPException( # pragma: no cover
|
|
225
|
+
status_code=status.HTTP_404_NOT_FOUND, detail=f"Project '{project}' not found."
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
"""
|
|
230
|
+
The project_id dependency is used in the following:
|
|
231
|
+
- EntityRepository
|
|
232
|
+
- ObservationRepository
|
|
233
|
+
- RelationRepository
|
|
234
|
+
- SearchRepository
|
|
235
|
+
- ProjectInfoRepository
|
|
236
|
+
"""
|
|
237
|
+
ProjectIdDep = Annotated[int, Depends(get_project_id)]
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
async def get_entity_repository(
|
|
241
|
+
session_maker: SessionMakerDep,
|
|
242
|
+
project_id: ProjectIdDep,
|
|
243
|
+
) -> EntityRepository:
|
|
244
|
+
"""Create an EntityRepository instance for the current project."""
|
|
245
|
+
return EntityRepository(session_maker, project_id=project_id)
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
EntityRepositoryDep = Annotated[EntityRepository, Depends(get_entity_repository)]
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
async def get_entity_repository_v2(
|
|
252
|
+
session_maker: SessionMakerDep,
|
|
253
|
+
project_id: ProjectIdPathDep,
|
|
254
|
+
) -> EntityRepository:
|
|
255
|
+
"""Create an EntityRepository instance for v2 API (uses integer project_id from path)."""
|
|
256
|
+
return EntityRepository(session_maker, project_id=project_id)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
EntityRepositoryV2Dep = Annotated[EntityRepository, Depends(get_entity_repository_v2)]
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
async def get_observation_repository(
|
|
263
|
+
session_maker: SessionMakerDep,
|
|
264
|
+
project_id: ProjectIdDep,
|
|
265
|
+
) -> ObservationRepository:
|
|
266
|
+
"""Create an ObservationRepository instance for the current project."""
|
|
267
|
+
return ObservationRepository(session_maker, project_id=project_id)
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
ObservationRepositoryDep = Annotated[ObservationRepository, Depends(get_observation_repository)]
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
async def get_observation_repository_v2(
|
|
274
|
+
session_maker: SessionMakerDep,
|
|
275
|
+
project_id: ProjectIdPathDep,
|
|
276
|
+
) -> ObservationRepository:
|
|
277
|
+
"""Create an ObservationRepository instance for v2 API."""
|
|
278
|
+
return ObservationRepository(session_maker, project_id=project_id)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
ObservationRepositoryV2Dep = Annotated[
|
|
282
|
+
ObservationRepository, Depends(get_observation_repository_v2)
|
|
283
|
+
]
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
async def get_relation_repository(
|
|
287
|
+
session_maker: SessionMakerDep,
|
|
288
|
+
project_id: ProjectIdDep,
|
|
289
|
+
) -> RelationRepository:
|
|
290
|
+
"""Create a RelationRepository instance for the current project."""
|
|
291
|
+
return RelationRepository(session_maker, project_id=project_id)
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
RelationRepositoryDep = Annotated[RelationRepository, Depends(get_relation_repository)]
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
async def get_relation_repository_v2(
|
|
298
|
+
session_maker: SessionMakerDep,
|
|
299
|
+
project_id: ProjectIdPathDep,
|
|
300
|
+
) -> RelationRepository:
|
|
301
|
+
"""Create a RelationRepository instance for v2 API."""
|
|
302
|
+
return RelationRepository(session_maker, project_id=project_id)
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
RelationRepositoryV2Dep = Annotated[RelationRepository, Depends(get_relation_repository_v2)]
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
async def get_search_repository(
|
|
309
|
+
session_maker: SessionMakerDep,
|
|
310
|
+
project_id: ProjectIdDep,
|
|
311
|
+
) -> SearchRepository:
|
|
312
|
+
"""Create a backend-specific SearchRepository instance for the current project.
|
|
313
|
+
|
|
314
|
+
Uses factory function to return SQLiteSearchRepository or PostgresSearchRepository
|
|
315
|
+
based on database backend configuration.
|
|
316
|
+
"""
|
|
317
|
+
return create_search_repository(session_maker, project_id=project_id)
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
SearchRepositoryDep = Annotated[SearchRepository, Depends(get_search_repository)]
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
async def get_search_repository_v2(
|
|
324
|
+
session_maker: SessionMakerDep,
|
|
325
|
+
project_id: ProjectIdPathDep,
|
|
326
|
+
) -> SearchRepository:
|
|
327
|
+
"""Create a SearchRepository instance for v2 API."""
|
|
328
|
+
return create_search_repository(session_maker, project_id=project_id)
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
SearchRepositoryV2Dep = Annotated[SearchRepository, Depends(get_search_repository_v2)]
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
# ProjectInfoRepository is deprecated and will be removed in a future version.
|
|
335
|
+
# Use ProjectRepository instead, which has the same functionality plus more project-specific operations.
|
|
336
|
+
|
|
337
|
+
## services
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
async def get_entity_parser(project_config: ProjectConfigDep) -> EntityParser:
|
|
341
|
+
return EntityParser(project_config.home)
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
EntityParserDep = Annotated["EntityParser", Depends(get_entity_parser)]
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
async def get_entity_parser_v2(project_config: ProjectConfigV2Dep) -> EntityParser:
|
|
348
|
+
return EntityParser(project_config.home)
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
EntityParserV2Dep = Annotated["EntityParser", Depends(get_entity_parser_v2)]
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
async def get_markdown_processor(
|
|
355
|
+
entity_parser: EntityParserDep, app_config: AppConfigDep
|
|
356
|
+
) -> MarkdownProcessor:
|
|
357
|
+
return MarkdownProcessor(entity_parser, app_config=app_config)
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
MarkdownProcessorDep = Annotated[MarkdownProcessor, Depends(get_markdown_processor)]
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
async def get_markdown_processor_v2(
|
|
364
|
+
entity_parser: EntityParserV2Dep, app_config: AppConfigDep
|
|
365
|
+
) -> MarkdownProcessor:
|
|
366
|
+
return MarkdownProcessor(entity_parser, app_config=app_config)
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
MarkdownProcessorV2Dep = Annotated[MarkdownProcessor, Depends(get_markdown_processor_v2)]
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
async def get_file_service(
|
|
373
|
+
project_config: ProjectConfigDep,
|
|
374
|
+
markdown_processor: MarkdownProcessorDep,
|
|
375
|
+
app_config: AppConfigDep,
|
|
376
|
+
) -> FileService:
|
|
377
|
+
file_service = FileService(project_config.home, markdown_processor, app_config=app_config)
|
|
378
|
+
logger.debug(
|
|
379
|
+
f"Created FileService for project: {project_config.name}, base_path: {project_config.home} "
|
|
380
|
+
)
|
|
381
|
+
return file_service
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
FileServiceDep = Annotated[FileService, Depends(get_file_service)]
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
async def get_file_service_v2(
|
|
388
|
+
project_config: ProjectConfigV2Dep,
|
|
389
|
+
markdown_processor: MarkdownProcessorV2Dep,
|
|
390
|
+
app_config: AppConfigDep,
|
|
391
|
+
) -> FileService:
|
|
392
|
+
file_service = FileService(project_config.home, markdown_processor, app_config=app_config)
|
|
393
|
+
logger.debug(
|
|
394
|
+
f"Created FileService for project: {project_config.name}, base_path: {project_config.home}"
|
|
395
|
+
)
|
|
396
|
+
return file_service
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
FileServiceV2Dep = Annotated[FileService, Depends(get_file_service_v2)]
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
async def get_entity_service(
|
|
403
|
+
entity_repository: EntityRepositoryDep,
|
|
404
|
+
observation_repository: ObservationRepositoryDep,
|
|
405
|
+
relation_repository: RelationRepositoryDep,
|
|
406
|
+
entity_parser: EntityParserDep,
|
|
407
|
+
file_service: FileServiceDep,
|
|
408
|
+
link_resolver: "LinkResolverDep",
|
|
409
|
+
search_service: "SearchServiceDep",
|
|
410
|
+
app_config: AppConfigDep,
|
|
411
|
+
) -> EntityService:
|
|
412
|
+
"""Create EntityService with repository."""
|
|
413
|
+
return EntityService(
|
|
414
|
+
entity_repository=entity_repository,
|
|
415
|
+
observation_repository=observation_repository,
|
|
416
|
+
relation_repository=relation_repository,
|
|
417
|
+
entity_parser=entity_parser,
|
|
418
|
+
file_service=file_service,
|
|
419
|
+
link_resolver=link_resolver,
|
|
420
|
+
search_service=search_service,
|
|
421
|
+
app_config=app_config,
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
EntityServiceDep = Annotated[EntityService, Depends(get_entity_service)]
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
async def get_entity_service_v2(
|
|
429
|
+
entity_repository: EntityRepositoryV2Dep,
|
|
430
|
+
observation_repository: ObservationRepositoryV2Dep,
|
|
431
|
+
relation_repository: RelationRepositoryV2Dep,
|
|
432
|
+
entity_parser: EntityParserV2Dep,
|
|
433
|
+
file_service: FileServiceV2Dep,
|
|
434
|
+
link_resolver: "LinkResolverV2Dep",
|
|
435
|
+
search_service: "SearchServiceV2Dep",
|
|
436
|
+
app_config: AppConfigDep,
|
|
437
|
+
) -> EntityService:
|
|
438
|
+
"""Create EntityService for v2 API."""
|
|
439
|
+
return EntityService(
|
|
440
|
+
entity_repository=entity_repository,
|
|
441
|
+
observation_repository=observation_repository,
|
|
442
|
+
relation_repository=relation_repository,
|
|
443
|
+
entity_parser=entity_parser,
|
|
444
|
+
file_service=file_service,
|
|
445
|
+
link_resolver=link_resolver,
|
|
446
|
+
search_service=search_service,
|
|
447
|
+
app_config=app_config,
|
|
448
|
+
)
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
EntityServiceV2Dep = Annotated[EntityService, Depends(get_entity_service_v2)]
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
async def get_search_service(
|
|
455
|
+
search_repository: SearchRepositoryDep,
|
|
456
|
+
entity_repository: EntityRepositoryDep,
|
|
457
|
+
file_service: FileServiceDep,
|
|
458
|
+
) -> SearchService:
|
|
459
|
+
"""Create SearchService with dependencies."""
|
|
460
|
+
return SearchService(search_repository, entity_repository, file_service)
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
SearchServiceDep = Annotated[SearchService, Depends(get_search_service)]
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
async def get_search_service_v2(
|
|
467
|
+
search_repository: SearchRepositoryV2Dep,
|
|
468
|
+
entity_repository: EntityRepositoryV2Dep,
|
|
469
|
+
file_service: FileServiceV2Dep,
|
|
470
|
+
) -> SearchService:
|
|
471
|
+
"""Create SearchService for v2 API."""
|
|
472
|
+
return SearchService(search_repository, entity_repository, file_service)
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
SearchServiceV2Dep = Annotated[SearchService, Depends(get_search_service_v2)]
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
async def get_link_resolver(
|
|
479
|
+
entity_repository: EntityRepositoryDep, search_service: SearchServiceDep
|
|
480
|
+
) -> LinkResolver:
|
|
481
|
+
return LinkResolver(entity_repository=entity_repository, search_service=search_service)
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
LinkResolverDep = Annotated[LinkResolver, Depends(get_link_resolver)]
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
async def get_link_resolver_v2(
|
|
488
|
+
entity_repository: EntityRepositoryV2Dep, search_service: SearchServiceV2Dep
|
|
489
|
+
) -> LinkResolver:
|
|
490
|
+
return LinkResolver(entity_repository=entity_repository, search_service=search_service)
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
LinkResolverV2Dep = Annotated[LinkResolver, Depends(get_link_resolver_v2)]
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
async def get_context_service(
|
|
497
|
+
search_repository: SearchRepositoryDep,
|
|
498
|
+
entity_repository: EntityRepositoryDep,
|
|
499
|
+
observation_repository: ObservationRepositoryDep,
|
|
500
|
+
) -> ContextService:
|
|
501
|
+
return ContextService(
|
|
502
|
+
search_repository=search_repository,
|
|
503
|
+
entity_repository=entity_repository,
|
|
504
|
+
observation_repository=observation_repository,
|
|
505
|
+
)
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
ContextServiceDep = Annotated[ContextService, Depends(get_context_service)]
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
async def get_context_service_v2(
|
|
512
|
+
search_repository: SearchRepositoryV2Dep,
|
|
513
|
+
entity_repository: EntityRepositoryV2Dep,
|
|
514
|
+
observation_repository: ObservationRepositoryV2Dep,
|
|
515
|
+
) -> ContextService:
|
|
516
|
+
"""Create ContextService for v2 API."""
|
|
517
|
+
return ContextService(
|
|
518
|
+
search_repository=search_repository,
|
|
519
|
+
entity_repository=entity_repository,
|
|
520
|
+
observation_repository=observation_repository,
|
|
521
|
+
)
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
ContextServiceV2Dep = Annotated[ContextService, Depends(get_context_service_v2)]
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
async def get_sync_service(
|
|
528
|
+
app_config: AppConfigDep,
|
|
529
|
+
entity_service: EntityServiceDep,
|
|
530
|
+
entity_parser: EntityParserDep,
|
|
531
|
+
entity_repository: EntityRepositoryDep,
|
|
532
|
+
relation_repository: RelationRepositoryDep,
|
|
533
|
+
project_repository: ProjectRepositoryDep,
|
|
534
|
+
search_service: SearchServiceDep,
|
|
535
|
+
file_service: FileServiceDep,
|
|
536
|
+
) -> SyncService: # pragma: no cover
|
|
537
|
+
"""
|
|
538
|
+
|
|
539
|
+
:rtype: object
|
|
540
|
+
"""
|
|
541
|
+
return SyncService(
|
|
542
|
+
app_config=app_config,
|
|
543
|
+
entity_service=entity_service,
|
|
544
|
+
entity_parser=entity_parser,
|
|
545
|
+
entity_repository=entity_repository,
|
|
546
|
+
relation_repository=relation_repository,
|
|
547
|
+
project_repository=project_repository,
|
|
548
|
+
search_service=search_service,
|
|
549
|
+
file_service=file_service,
|
|
550
|
+
)
|
|
551
|
+
|
|
552
|
+
|
|
553
|
+
SyncServiceDep = Annotated[SyncService, Depends(get_sync_service)]
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
async def get_sync_service_v2(
|
|
557
|
+
app_config: AppConfigDep,
|
|
558
|
+
entity_service: EntityServiceV2Dep,
|
|
559
|
+
entity_parser: EntityParserV2Dep,
|
|
560
|
+
entity_repository: EntityRepositoryV2Dep,
|
|
561
|
+
relation_repository: RelationRepositoryV2Dep,
|
|
562
|
+
project_repository: ProjectRepositoryDep,
|
|
563
|
+
search_service: SearchServiceV2Dep,
|
|
564
|
+
file_service: FileServiceV2Dep,
|
|
565
|
+
) -> SyncService: # pragma: no cover
|
|
566
|
+
"""Create SyncService for v2 API."""
|
|
567
|
+
return SyncService(
|
|
568
|
+
app_config=app_config,
|
|
569
|
+
entity_service=entity_service,
|
|
570
|
+
entity_parser=entity_parser,
|
|
571
|
+
entity_repository=entity_repository,
|
|
572
|
+
relation_repository=relation_repository,
|
|
573
|
+
project_repository=project_repository,
|
|
574
|
+
search_service=search_service,
|
|
575
|
+
file_service=file_service,
|
|
576
|
+
)
|
|
577
|
+
|
|
578
|
+
|
|
579
|
+
SyncServiceV2Dep = Annotated[SyncService, Depends(get_sync_service_v2)]
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
async def get_project_service(
|
|
583
|
+
project_repository: ProjectRepositoryDep,
|
|
584
|
+
) -> ProjectService:
|
|
585
|
+
"""Create ProjectService with repository."""
|
|
586
|
+
return ProjectService(repository=project_repository)
|
|
587
|
+
|
|
588
|
+
|
|
589
|
+
ProjectServiceDep = Annotated[ProjectService, Depends(get_project_service)]
|
|
590
|
+
|
|
591
|
+
|
|
592
|
+
async def get_directory_service(
|
|
593
|
+
entity_repository: EntityRepositoryDep,
|
|
594
|
+
) -> DirectoryService:
|
|
595
|
+
"""Create DirectoryService with dependencies."""
|
|
596
|
+
return DirectoryService(
|
|
597
|
+
entity_repository=entity_repository,
|
|
598
|
+
)
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
DirectoryServiceDep = Annotated[DirectoryService, Depends(get_directory_service)]
|
|
602
|
+
|
|
603
|
+
|
|
604
|
+
async def get_directory_service_v2(
|
|
605
|
+
entity_repository: EntityRepositoryV2Dep,
|
|
606
|
+
) -> DirectoryService:
|
|
607
|
+
"""Create DirectoryService for v2 API (uses integer project_id from path)."""
|
|
608
|
+
return DirectoryService(
|
|
609
|
+
entity_repository=entity_repository,
|
|
610
|
+
)
|
|
611
|
+
|
|
612
|
+
|
|
613
|
+
DirectoryServiceV2Dep = Annotated[DirectoryService, Depends(get_directory_service_v2)]
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
# Import
|
|
617
|
+
|
|
618
|
+
|
|
619
|
+
async def get_chatgpt_importer(
|
|
620
|
+
project_config: ProjectConfigDep, markdown_processor: MarkdownProcessorDep
|
|
621
|
+
) -> ChatGPTImporter:
|
|
622
|
+
"""Create ChatGPTImporter with dependencies."""
|
|
623
|
+
return ChatGPTImporter(project_config.home, markdown_processor)
|
|
624
|
+
|
|
625
|
+
|
|
626
|
+
ChatGPTImporterDep = Annotated[ChatGPTImporter, Depends(get_chatgpt_importer)]
|
|
627
|
+
|
|
628
|
+
|
|
629
|
+
async def get_claude_conversations_importer(
|
|
630
|
+
project_config: ProjectConfigDep, markdown_processor: MarkdownProcessorDep
|
|
631
|
+
) -> ClaudeConversationsImporter:
|
|
632
|
+
"""Create ChatGPTImporter with dependencies."""
|
|
633
|
+
return ClaudeConversationsImporter(project_config.home, markdown_processor)
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
ClaudeConversationsImporterDep = Annotated[
|
|
637
|
+
ClaudeConversationsImporter, Depends(get_claude_conversations_importer)
|
|
638
|
+
]
|
|
639
|
+
|
|
640
|
+
|
|
641
|
+
async def get_claude_projects_importer(
|
|
642
|
+
project_config: ProjectConfigDep, markdown_processor: MarkdownProcessorDep
|
|
643
|
+
) -> ClaudeProjectsImporter:
|
|
644
|
+
"""Create ChatGPTImporter with dependencies."""
|
|
645
|
+
return ClaudeProjectsImporter(project_config.home, markdown_processor)
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+
ClaudeProjectsImporterDep = Annotated[ClaudeProjectsImporter, Depends(get_claude_projects_importer)]
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
async def get_memory_json_importer(
|
|
652
|
+
project_config: ProjectConfigDep, markdown_processor: MarkdownProcessorDep
|
|
653
|
+
) -> MemoryJsonImporter:
|
|
654
|
+
"""Create ChatGPTImporter with dependencies."""
|
|
655
|
+
return MemoryJsonImporter(project_config.home, markdown_processor)
|
|
656
|
+
|
|
657
|
+
|
|
658
|
+
MemoryJsonImporterDep = Annotated[MemoryJsonImporter, Depends(get_memory_json_importer)]
|
|
659
|
+
|
|
660
|
+
|
|
661
|
+
# V2 Import dependencies
|
|
662
|
+
|
|
663
|
+
|
|
664
|
+
async def get_chatgpt_importer_v2(
|
|
665
|
+
project_config: ProjectConfigV2Dep, markdown_processor: MarkdownProcessorV2Dep
|
|
666
|
+
) -> ChatGPTImporter:
|
|
667
|
+
"""Create ChatGPTImporter with v2 dependencies."""
|
|
668
|
+
return ChatGPTImporter(project_config.home, markdown_processor)
|
|
669
|
+
|
|
670
|
+
|
|
671
|
+
ChatGPTImporterV2Dep = Annotated[ChatGPTImporter, Depends(get_chatgpt_importer_v2)]
|
|
672
|
+
|
|
673
|
+
|
|
674
|
+
async def get_claude_conversations_importer_v2(
|
|
675
|
+
project_config: ProjectConfigV2Dep, markdown_processor: MarkdownProcessorV2Dep
|
|
676
|
+
) -> ClaudeConversationsImporter:
|
|
677
|
+
"""Create ClaudeConversationsImporter with v2 dependencies."""
|
|
678
|
+
return ClaudeConversationsImporter(project_config.home, markdown_processor)
|
|
679
|
+
|
|
680
|
+
|
|
681
|
+
ClaudeConversationsImporterV2Dep = Annotated[
|
|
682
|
+
ClaudeConversationsImporter, Depends(get_claude_conversations_importer_v2)
|
|
683
|
+
]
|
|
684
|
+
|
|
685
|
+
|
|
686
|
+
async def get_claude_projects_importer_v2(
|
|
687
|
+
project_config: ProjectConfigV2Dep, markdown_processor: MarkdownProcessorV2Dep
|
|
688
|
+
) -> ClaudeProjectsImporter:
|
|
689
|
+
"""Create ClaudeProjectsImporter with v2 dependencies."""
|
|
690
|
+
return ClaudeProjectsImporter(project_config.home, markdown_processor)
|
|
691
|
+
|
|
692
|
+
|
|
693
|
+
ClaudeProjectsImporterV2Dep = Annotated[
|
|
694
|
+
ClaudeProjectsImporter, Depends(get_claude_projects_importer_v2)
|
|
695
|
+
]
|
|
696
|
+
|
|
697
|
+
|
|
698
|
+
async def get_memory_json_importer_v2(
|
|
699
|
+
project_config: ProjectConfigV2Dep, markdown_processor: MarkdownProcessorV2Dep
|
|
700
|
+
) -> MemoryJsonImporter:
|
|
701
|
+
"""Create MemoryJsonImporter with v2 dependencies."""
|
|
702
|
+
return MemoryJsonImporter(project_config.home, markdown_processor)
|
|
703
|
+
|
|
704
|
+
|
|
705
|
+
MemoryJsonImporterV2Dep = Annotated[MemoryJsonImporter, Depends(get_memory_json_importer_v2)]
|