kodit 0.4.2__py3-none-any.whl → 0.5.0__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 kodit might be problematic. Click here for more details.
- kodit/_version.py +2 -2
- kodit/app.py +59 -24
- kodit/application/factories/reporting_factory.py +16 -7
- kodit/application/factories/server_factory.py +311 -0
- kodit/application/services/code_search_application_service.py +144 -0
- kodit/application/services/commit_indexing_application_service.py +543 -0
- kodit/application/services/indexing_worker_service.py +13 -46
- kodit/application/services/queue_service.py +24 -3
- kodit/application/services/reporting.py +70 -54
- kodit/application/services/sync_scheduler.py +15 -31
- kodit/cli.py +2 -763
- kodit/cli_utils.py +2 -9
- kodit/config.py +3 -96
- kodit/database.py +38 -1
- kodit/domain/entities/__init__.py +276 -0
- kodit/domain/entities/git.py +190 -0
- kodit/domain/factories/__init__.py +1 -0
- kodit/domain/factories/git_repo_factory.py +76 -0
- kodit/domain/protocols.py +270 -46
- kodit/domain/services/bm25_service.py +5 -1
- kodit/domain/services/embedding_service.py +3 -0
- kodit/domain/services/git_repository_service.py +429 -0
- kodit/domain/services/git_service.py +300 -0
- kodit/domain/services/task_status_query_service.py +19 -0
- kodit/domain/value_objects.py +113 -147
- kodit/infrastructure/api/client/__init__.py +0 -2
- kodit/infrastructure/api/v1/__init__.py +0 -4
- kodit/infrastructure/api/v1/dependencies.py +105 -44
- kodit/infrastructure/api/v1/routers/__init__.py +0 -6
- kodit/infrastructure/api/v1/routers/commits.py +271 -0
- kodit/infrastructure/api/v1/routers/queue.py +2 -2
- kodit/infrastructure/api/v1/routers/repositories.py +282 -0
- kodit/infrastructure/api/v1/routers/search.py +31 -14
- kodit/infrastructure/api/v1/schemas/__init__.py +0 -24
- kodit/infrastructure/api/v1/schemas/commit.py +96 -0
- kodit/infrastructure/api/v1/schemas/context.py +2 -0
- kodit/infrastructure/api/v1/schemas/repository.py +128 -0
- kodit/infrastructure/api/v1/schemas/search.py +12 -9
- kodit/infrastructure/api/v1/schemas/snippet.py +58 -0
- kodit/infrastructure/api/v1/schemas/tag.py +31 -0
- kodit/infrastructure/api/v1/schemas/task_status.py +41 -0
- kodit/infrastructure/bm25/local_bm25_repository.py +16 -4
- kodit/infrastructure/bm25/vectorchord_bm25_repository.py +68 -52
- kodit/infrastructure/cloning/git/git_python_adaptor.py +467 -0
- kodit/infrastructure/cloning/git/working_copy.py +10 -3
- kodit/infrastructure/embedding/embedding_factory.py +3 -2
- kodit/infrastructure/embedding/local_vector_search_repository.py +1 -1
- kodit/infrastructure/embedding/vectorchord_vector_search_repository.py +111 -84
- kodit/infrastructure/enrichment/litellm_enrichment_provider.py +19 -26
- kodit/infrastructure/enrichment/local_enrichment_provider.py +41 -30
- kodit/infrastructure/indexing/fusion_service.py +1 -1
- kodit/infrastructure/mappers/git_mapper.py +193 -0
- kodit/infrastructure/mappers/snippet_mapper.py +106 -0
- kodit/infrastructure/mappers/task_mapper.py +5 -44
- kodit/infrastructure/mappers/task_status_mapper.py +85 -0
- kodit/infrastructure/reporting/db_progress.py +23 -0
- kodit/infrastructure/reporting/log_progress.py +13 -38
- kodit/infrastructure/reporting/telemetry_progress.py +21 -0
- kodit/infrastructure/slicing/slicer.py +32 -31
- kodit/infrastructure/sqlalchemy/embedding_repository.py +43 -23
- kodit/infrastructure/sqlalchemy/entities.py +428 -131
- kodit/infrastructure/sqlalchemy/git_branch_repository.py +263 -0
- kodit/infrastructure/sqlalchemy/git_commit_repository.py +337 -0
- kodit/infrastructure/sqlalchemy/git_repository.py +252 -0
- kodit/infrastructure/sqlalchemy/git_tag_repository.py +257 -0
- kodit/infrastructure/sqlalchemy/snippet_v2_repository.py +484 -0
- kodit/infrastructure/sqlalchemy/task_repository.py +29 -23
- kodit/infrastructure/sqlalchemy/task_status_repository.py +91 -0
- kodit/infrastructure/sqlalchemy/unit_of_work.py +10 -14
- kodit/mcp.py +12 -26
- kodit/migrations/env.py +1 -1
- kodit/migrations/versions/04b80f802e0c_foreign_key_review.py +100 -0
- kodit/migrations/versions/7f15f878c3a1_add_new_git_entities.py +690 -0
- kodit/migrations/versions/b9cd1c3fd762_add_task_status.py +77 -0
- kodit/migrations/versions/f9e5ef5e688f_add_git_commits_number.py +43 -0
- kodit/py.typed +0 -0
- kodit/utils/dump_openapi.py +7 -4
- kodit/utils/path_utils.py +29 -0
- {kodit-0.4.2.dist-info → kodit-0.5.0.dist-info}/METADATA +3 -3
- kodit-0.5.0.dist-info/RECORD +137 -0
- kodit/application/factories/code_indexing_factory.py +0 -193
- kodit/application/services/auto_indexing_service.py +0 -103
- kodit/application/services/code_indexing_application_service.py +0 -393
- kodit/domain/entities.py +0 -323
- kodit/domain/services/index_query_service.py +0 -70
- kodit/domain/services/index_service.py +0 -267
- kodit/infrastructure/api/client/index_client.py +0 -57
- kodit/infrastructure/api/v1/routers/indexes.py +0 -119
- kodit/infrastructure/api/v1/schemas/index.py +0 -101
- kodit/infrastructure/bm25/bm25_factory.py +0 -28
- kodit/infrastructure/cloning/__init__.py +0 -1
- kodit/infrastructure/cloning/metadata.py +0 -98
- kodit/infrastructure/mappers/index_mapper.py +0 -345
- kodit/infrastructure/reporting/tdqm_progress.py +0 -73
- kodit/infrastructure/slicing/language_detection_service.py +0 -18
- kodit/infrastructure/sqlalchemy/index_repository.py +0 -646
- kodit-0.4.2.dist-info/RECORD +0 -119
- {kodit-0.4.2.dist-info → kodit-0.5.0.dist-info}/WHEEL +0 -0
- {kodit-0.4.2.dist-info → kodit-0.5.0.dist-info}/entry_points.txt +0 -0
- {kodit-0.4.2.dist-info → kodit-0.5.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
"""Repository management router for the REST API."""
|
|
2
|
+
|
|
3
|
+
from fastapi import APIRouter, Depends, HTTPException
|
|
4
|
+
|
|
5
|
+
from kodit.infrastructure.api.middleware.auth import api_key_auth
|
|
6
|
+
from kodit.infrastructure.api.v1.dependencies import (
|
|
7
|
+
CommitIndexingAppServiceDep,
|
|
8
|
+
GitBranchRepositoryDep,
|
|
9
|
+
GitCommitRepositoryDep,
|
|
10
|
+
GitRepositoryDep,
|
|
11
|
+
GitTagRepositoryDep,
|
|
12
|
+
TaskStatusQueryServiceDep,
|
|
13
|
+
)
|
|
14
|
+
from kodit.infrastructure.api.v1.schemas.repository import (
|
|
15
|
+
RepositoryBranchData,
|
|
16
|
+
RepositoryCommitData,
|
|
17
|
+
RepositoryCreateRequest,
|
|
18
|
+
RepositoryData,
|
|
19
|
+
RepositoryDetailsResponse,
|
|
20
|
+
RepositoryListResponse,
|
|
21
|
+
RepositoryResponse,
|
|
22
|
+
)
|
|
23
|
+
from kodit.infrastructure.api.v1.schemas.tag import (
|
|
24
|
+
TagAttributes,
|
|
25
|
+
TagData,
|
|
26
|
+
TagListResponse,
|
|
27
|
+
TagResponse,
|
|
28
|
+
)
|
|
29
|
+
from kodit.infrastructure.api.v1.schemas.task_status import (
|
|
30
|
+
TaskStatusAttributes,
|
|
31
|
+
TaskStatusData,
|
|
32
|
+
TaskStatusListResponse,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
router = APIRouter(
|
|
36
|
+
prefix="/api/v1/repositories",
|
|
37
|
+
tags=["repositories"],
|
|
38
|
+
dependencies=[Depends(api_key_auth)],
|
|
39
|
+
responses={
|
|
40
|
+
401: {"description": "Unauthorized"},
|
|
41
|
+
422: {"description": "Invalid request"},
|
|
42
|
+
},
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _raise_not_found_error(detail: str) -> None:
|
|
47
|
+
"""Raise repository not found error."""
|
|
48
|
+
raise HTTPException(status_code=404, detail=detail)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@router.get("", summary="List repositories")
|
|
52
|
+
async def list_repositories(
|
|
53
|
+
git_repository: GitRepositoryDep,
|
|
54
|
+
) -> RepositoryListResponse:
|
|
55
|
+
"""List all cloned repositories."""
|
|
56
|
+
repos = await git_repository.get_all()
|
|
57
|
+
return RepositoryListResponse(
|
|
58
|
+
data=[RepositoryData.from_git_repo(repo) for repo in repos]
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@router.post("", status_code=201, summary="Create repository")
|
|
63
|
+
async def create_repository(
|
|
64
|
+
request: RepositoryCreateRequest,
|
|
65
|
+
service: CommitIndexingAppServiceDep,
|
|
66
|
+
) -> RepositoryResponse:
|
|
67
|
+
"""Clone a new repository and perform initial mapping."""
|
|
68
|
+
try:
|
|
69
|
+
remote_uri = request.data.attributes.remote_uri
|
|
70
|
+
|
|
71
|
+
repo = await service.create_git_repository(remote_uri)
|
|
72
|
+
|
|
73
|
+
return RepositoryResponse(data=RepositoryData.from_git_repo(repo))
|
|
74
|
+
except ValueError as e:
|
|
75
|
+
if "already exists" in str(e):
|
|
76
|
+
raise HTTPException(status_code=409, detail=str(e)) from e
|
|
77
|
+
raise HTTPException(status_code=400, detail=str(e)) from e
|
|
78
|
+
except Exception as e:
|
|
79
|
+
msg = f"Failed to clone repository: {e}"
|
|
80
|
+
raise HTTPException(status_code=500, detail=msg) from e
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@router.get(
|
|
84
|
+
"/{repo_id}",
|
|
85
|
+
summary="Get repository",
|
|
86
|
+
responses={404: {"description": "Repository not found"}},
|
|
87
|
+
)
|
|
88
|
+
async def get_repository(
|
|
89
|
+
repo_id: str,
|
|
90
|
+
git_repository: GitRepositoryDep,
|
|
91
|
+
git_commit_repository: GitCommitRepositoryDep,
|
|
92
|
+
git_branch_repository: GitBranchRepositoryDep,
|
|
93
|
+
) -> RepositoryDetailsResponse:
|
|
94
|
+
"""Get repository details including branches and recent commits."""
|
|
95
|
+
repo = await git_repository.get_by_id(int(repo_id))
|
|
96
|
+
if not repo:
|
|
97
|
+
raise HTTPException(status_code=404, detail="Repository not found")
|
|
98
|
+
|
|
99
|
+
# Get all commits for this repository from the commit repository
|
|
100
|
+
repo_commits = await git_commit_repository.get_by_repo_id(int(repo_id))
|
|
101
|
+
commits_by_sha = {commit.commit_sha: commit for commit in repo_commits}
|
|
102
|
+
|
|
103
|
+
# Get recent commits from the tracking branch's head commit
|
|
104
|
+
recent_commits = []
|
|
105
|
+
if repo.tracking_branch and repo.tracking_branch.head_commit:
|
|
106
|
+
# For simplicity, just show the head commit and traverse back if needed
|
|
107
|
+
current_commit = repo.tracking_branch.head_commit
|
|
108
|
+
recent_commits = [current_commit]
|
|
109
|
+
|
|
110
|
+
# Traverse parent commits for more recent commits (up to 10)
|
|
111
|
+
current_sha = current_commit.parent_commit_sha
|
|
112
|
+
while current_sha and len(recent_commits) < 10:
|
|
113
|
+
parent_commit = commits_by_sha.get(current_sha)
|
|
114
|
+
if parent_commit:
|
|
115
|
+
recent_commits.append(parent_commit)
|
|
116
|
+
current_sha = parent_commit.parent_commit_sha
|
|
117
|
+
else:
|
|
118
|
+
break
|
|
119
|
+
|
|
120
|
+
# Get commit count for the repository using the commit repository
|
|
121
|
+
commit_count = await git_commit_repository.count_by_repo_id(int(repo_id))
|
|
122
|
+
|
|
123
|
+
# Get branches for the repository using the branch repository
|
|
124
|
+
repo_branches = await git_branch_repository.get_by_repo_id(int(repo_id))
|
|
125
|
+
|
|
126
|
+
# Get commit counts for all branches using the commit repository
|
|
127
|
+
branch_data = []
|
|
128
|
+
for branch in repo_branches:
|
|
129
|
+
# For simplicity, use the total commit count for all branches
|
|
130
|
+
# In a more advanced implementation, we would traverse each branch's history
|
|
131
|
+
branch_commit_count = commit_count
|
|
132
|
+
|
|
133
|
+
branch_data.append(
|
|
134
|
+
RepositoryBranchData(
|
|
135
|
+
name=branch.name,
|
|
136
|
+
is_default=branch.name == repo.tracking_branch.name
|
|
137
|
+
if repo.tracking_branch
|
|
138
|
+
else False,
|
|
139
|
+
commit_count=branch_commit_count,
|
|
140
|
+
)
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
return RepositoryDetailsResponse(
|
|
144
|
+
data=RepositoryData.from_git_repo(repo),
|
|
145
|
+
branches=branch_data,
|
|
146
|
+
recent_commits=[
|
|
147
|
+
RepositoryCommitData(
|
|
148
|
+
sha=commit.commit_sha,
|
|
149
|
+
message=commit.message,
|
|
150
|
+
author=commit.author,
|
|
151
|
+
timestamp=commit.date,
|
|
152
|
+
)
|
|
153
|
+
for commit in recent_commits
|
|
154
|
+
],
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@router.get(
|
|
159
|
+
"/{repo_id}/status",
|
|
160
|
+
responses={404: {"description": "Index not found"}},
|
|
161
|
+
)
|
|
162
|
+
async def get_index_status(
|
|
163
|
+
repo_id: int,
|
|
164
|
+
status_service: TaskStatusQueryServiceDep,
|
|
165
|
+
) -> TaskStatusListResponse:
|
|
166
|
+
"""Get the status of tasks for an index."""
|
|
167
|
+
# Get all task statuses for this index
|
|
168
|
+
progress_trackers = await status_service.get_index_status(repo_id)
|
|
169
|
+
|
|
170
|
+
# Convert progress trackers to API response format
|
|
171
|
+
task_statuses = []
|
|
172
|
+
for _i, status in enumerate(progress_trackers):
|
|
173
|
+
task_statuses.append(
|
|
174
|
+
TaskStatusData(
|
|
175
|
+
id=status.id,
|
|
176
|
+
attributes=TaskStatusAttributes(
|
|
177
|
+
step=status.operation,
|
|
178
|
+
state=status.state,
|
|
179
|
+
progress=status.completion_percent,
|
|
180
|
+
total=status.total,
|
|
181
|
+
current=status.current,
|
|
182
|
+
created_at=status.created_at,
|
|
183
|
+
updated_at=status.updated_at,
|
|
184
|
+
error=status.error or "",
|
|
185
|
+
message=status.message,
|
|
186
|
+
),
|
|
187
|
+
)
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
return TaskStatusListResponse(data=task_statuses)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
@router.get(
|
|
194
|
+
"/{repo_id}/tags",
|
|
195
|
+
summary="List repository tags",
|
|
196
|
+
responses={404: {"description": "Repository not found"}},
|
|
197
|
+
)
|
|
198
|
+
async def list_repository_tags(
|
|
199
|
+
repo_id: str,
|
|
200
|
+
git_repository: GitRepositoryDep,
|
|
201
|
+
git_tag_repository: GitTagRepositoryDep,
|
|
202
|
+
) -> TagListResponse:
|
|
203
|
+
"""List all tags for a repository."""
|
|
204
|
+
repo = await git_repository.get_by_id(int(repo_id))
|
|
205
|
+
if not repo:
|
|
206
|
+
raise HTTPException(status_code=404, detail="Repository not found")
|
|
207
|
+
|
|
208
|
+
# Tags are now stored in a dedicated repository
|
|
209
|
+
tags = await git_tag_repository.get_by_repo_id(int(repo_id))
|
|
210
|
+
|
|
211
|
+
return TagListResponse(
|
|
212
|
+
data=[
|
|
213
|
+
TagData(
|
|
214
|
+
type="tag",
|
|
215
|
+
id=tag.id,
|
|
216
|
+
attributes=TagAttributes(
|
|
217
|
+
name=tag.name,
|
|
218
|
+
target_commit_sha=tag.target_commit.commit_sha,
|
|
219
|
+
is_version_tag=tag.is_version_tag,
|
|
220
|
+
),
|
|
221
|
+
)
|
|
222
|
+
for tag in tags
|
|
223
|
+
]
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
@router.get(
|
|
228
|
+
"/{repo_id}/tags/{tag_id}",
|
|
229
|
+
summary="Get repository tag",
|
|
230
|
+
responses={404: {"description": "Repository or tag not found"}},
|
|
231
|
+
)
|
|
232
|
+
async def get_repository_tag(
|
|
233
|
+
repo_id: str,
|
|
234
|
+
tag_id: str,
|
|
235
|
+
git_repository: GitRepositoryDep,
|
|
236
|
+
git_tag_repository: GitTagRepositoryDep,
|
|
237
|
+
) -> TagResponse:
|
|
238
|
+
"""Get a specific tag for a repository."""
|
|
239
|
+
repo = await git_repository.get_by_id(int(repo_id))
|
|
240
|
+
if not repo:
|
|
241
|
+
raise HTTPException(status_code=404, detail="Repository not found")
|
|
242
|
+
|
|
243
|
+
# Get all tags and find the specific one by ID
|
|
244
|
+
tags = await git_tag_repository.get_by_repo_id(int(repo_id))
|
|
245
|
+
tag = next((t for t in tags if t.id == tag_id), None)
|
|
246
|
+
if not tag:
|
|
247
|
+
raise HTTPException(status_code=404, detail="Tag not found")
|
|
248
|
+
|
|
249
|
+
return TagResponse(
|
|
250
|
+
data=TagData(
|
|
251
|
+
type="tag",
|
|
252
|
+
id=tag.id,
|
|
253
|
+
attributes=TagAttributes(
|
|
254
|
+
name=tag.name,
|
|
255
|
+
target_commit_sha=tag.target_commit.commit_sha,
|
|
256
|
+
is_version_tag=tag.is_version_tag,
|
|
257
|
+
),
|
|
258
|
+
)
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
@router.delete(
|
|
263
|
+
"/{repo_id}",
|
|
264
|
+
status_code=204,
|
|
265
|
+
summary="Delete repository",
|
|
266
|
+
responses={404: {"description": "Repository not found"}},
|
|
267
|
+
)
|
|
268
|
+
async def delete_repository(
|
|
269
|
+
repo_id: str,
|
|
270
|
+
service: CommitIndexingAppServiceDep,
|
|
271
|
+
) -> None:
|
|
272
|
+
"""Delete a repository and all its associated data."""
|
|
273
|
+
try:
|
|
274
|
+
repo_id_int = int(repo_id)
|
|
275
|
+
deleted = await service.delete_git_repository(repo_id_int)
|
|
276
|
+
if not deleted:
|
|
277
|
+
_raise_not_found_error("Repository not found")
|
|
278
|
+
except ValueError:
|
|
279
|
+
raise HTTPException(status_code=400, detail="Invalid repository ID") from None
|
|
280
|
+
except Exception as e:
|
|
281
|
+
msg = f"Failed to delete repository: {e}"
|
|
282
|
+
raise HTTPException(status_code=500, detail=msg) from e
|
|
@@ -3,15 +3,18 @@
|
|
|
3
3
|
from fastapi import APIRouter
|
|
4
4
|
|
|
5
5
|
from kodit.domain.value_objects import MultiSearchRequest, SnippetSearchFilters
|
|
6
|
-
from kodit.infrastructure.api.v1.dependencies import
|
|
7
|
-
IndexingAppServiceDep,
|
|
8
|
-
)
|
|
6
|
+
from kodit.infrastructure.api.v1.dependencies import CodeSearchAppServiceDep
|
|
9
7
|
from kodit.infrastructure.api.v1.schemas.search import (
|
|
10
8
|
SearchRequest,
|
|
11
9
|
SearchResponse,
|
|
12
10
|
SnippetAttributes,
|
|
13
11
|
SnippetData,
|
|
14
12
|
)
|
|
13
|
+
from kodit.infrastructure.api.v1.schemas.snippet import (
|
|
14
|
+
EnrichmentSchema,
|
|
15
|
+
GitFileSchema,
|
|
16
|
+
SnippetContentSchema,
|
|
17
|
+
)
|
|
15
18
|
|
|
16
19
|
router = APIRouter(tags=["search"])
|
|
17
20
|
|
|
@@ -19,7 +22,7 @@ router = APIRouter(tags=["search"])
|
|
|
19
22
|
@router.post("/api/v1/search")
|
|
20
23
|
async def search_snippets(
|
|
21
24
|
request: SearchRequest,
|
|
22
|
-
|
|
25
|
+
search_application_service: CodeSearchAppServiceDep,
|
|
23
26
|
) -> SearchResponse:
|
|
24
27
|
"""Search code snippets with filters matching MCP tool."""
|
|
25
28
|
# Convert API request to domain request
|
|
@@ -50,23 +53,37 @@ async def search_snippets(
|
|
|
50
53
|
)
|
|
51
54
|
|
|
52
55
|
# Execute search using application service
|
|
53
|
-
results = await
|
|
56
|
+
results = await search_application_service.search(domain_request)
|
|
54
57
|
|
|
55
58
|
return SearchResponse(
|
|
56
59
|
data=[
|
|
57
60
|
SnippetData(
|
|
58
61
|
type="snippet",
|
|
59
|
-
id=result.id,
|
|
62
|
+
id=result.snippet.id,
|
|
60
63
|
attributes=SnippetAttributes(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
+
created_at=result.snippet.created_at,
|
|
65
|
+
updated_at=result.snippet.updated_at,
|
|
66
|
+
derives_from=[
|
|
67
|
+
GitFileSchema(
|
|
68
|
+
blob_sha=file.blob_sha,
|
|
69
|
+
path=file.path,
|
|
70
|
+
mime_type=file.mime_type,
|
|
71
|
+
size=file.size,
|
|
72
|
+
)
|
|
73
|
+
for file in result.snippet.derives_from
|
|
74
|
+
],
|
|
75
|
+
content=SnippetContentSchema(
|
|
76
|
+
value=result.snippet.content,
|
|
77
|
+
language=result.snippet.extension,
|
|
78
|
+
),
|
|
79
|
+
enrichments=[
|
|
80
|
+
EnrichmentSchema(
|
|
81
|
+
type=enrichment.type.value,
|
|
82
|
+
content=enrichment.content,
|
|
83
|
+
)
|
|
84
|
+
for enrichment in result.snippet.enrichments
|
|
85
|
+
],
|
|
64
86
|
original_scores=result.original_scores,
|
|
65
|
-
source_uri=result.source_uri,
|
|
66
|
-
relative_path=result.relative_path,
|
|
67
|
-
language=result.language,
|
|
68
|
-
authors=result.authors,
|
|
69
|
-
summary=result.summary,
|
|
70
87
|
),
|
|
71
88
|
)
|
|
72
89
|
for result in results
|
|
@@ -1,25 +1 @@
|
|
|
1
1
|
"""JSON:API schemas for the REST API."""
|
|
2
|
-
|
|
3
|
-
from .index import (
|
|
4
|
-
IndexCreateRequest,
|
|
5
|
-
IndexDetailResponse,
|
|
6
|
-
IndexListResponse,
|
|
7
|
-
IndexResponse,
|
|
8
|
-
)
|
|
9
|
-
from .search import (
|
|
10
|
-
SearchRequest,
|
|
11
|
-
SearchResponse,
|
|
12
|
-
SearchResponseWithIncluded,
|
|
13
|
-
SnippetDetailResponse,
|
|
14
|
-
)
|
|
15
|
-
|
|
16
|
-
__all__ = [
|
|
17
|
-
"IndexCreateRequest",
|
|
18
|
-
"IndexDetailResponse",
|
|
19
|
-
"IndexListResponse",
|
|
20
|
-
"IndexResponse",
|
|
21
|
-
"SearchRequest",
|
|
22
|
-
"SearchResponse",
|
|
23
|
-
"SearchResponseWithIncluded",
|
|
24
|
-
"SnippetDetailResponse",
|
|
25
|
-
]
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""Commit JSON-API schemas."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class GitFileData(BaseModel):
|
|
9
|
+
"""Git file data."""
|
|
10
|
+
|
|
11
|
+
blob_sha: str
|
|
12
|
+
path: str
|
|
13
|
+
mime_type: str
|
|
14
|
+
size: int
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class CommitAttributes(BaseModel):
|
|
18
|
+
"""Commit attributes following JSON-API spec."""
|
|
19
|
+
|
|
20
|
+
commit_sha: str
|
|
21
|
+
date: datetime
|
|
22
|
+
message: str
|
|
23
|
+
parent_commit_sha: str
|
|
24
|
+
author: str
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class CommitData(BaseModel):
|
|
28
|
+
"""Commit data following JSON-API spec."""
|
|
29
|
+
|
|
30
|
+
type: str = "commit"
|
|
31
|
+
id: str
|
|
32
|
+
attributes: CommitAttributes
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class CommitResponse(BaseModel):
|
|
36
|
+
"""Single commit response following JSON-API spec."""
|
|
37
|
+
|
|
38
|
+
data: CommitData
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class CommitListResponse(BaseModel):
|
|
42
|
+
"""Commit list response following JSON-API spec."""
|
|
43
|
+
|
|
44
|
+
data: list[CommitData]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class FileAttributes(BaseModel):
|
|
48
|
+
"""File attributes following JSON-API spec."""
|
|
49
|
+
|
|
50
|
+
blob_sha: str
|
|
51
|
+
path: str
|
|
52
|
+
mime_type: str
|
|
53
|
+
size: int
|
|
54
|
+
extension: str
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class FileData(BaseModel):
|
|
58
|
+
"""File data following JSON-API spec."""
|
|
59
|
+
|
|
60
|
+
type: str = "file"
|
|
61
|
+
id: str
|
|
62
|
+
attributes: FileAttributes
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class FileResponse(BaseModel):
|
|
66
|
+
"""Single file response following JSON-API spec."""
|
|
67
|
+
|
|
68
|
+
data: FileData
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class FileListResponse(BaseModel):
|
|
72
|
+
"""File list response following JSON-API spec."""
|
|
73
|
+
|
|
74
|
+
data: list[FileData]
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class EmbeddingAttributes(BaseModel):
|
|
78
|
+
"""Embedding attributes following JSON-API spec."""
|
|
79
|
+
|
|
80
|
+
snippet_sha: str
|
|
81
|
+
embedding_type: str
|
|
82
|
+
embedding: list[float]
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class EmbeddingData(BaseModel):
|
|
86
|
+
"""Embedding data following JSON-API spec."""
|
|
87
|
+
|
|
88
|
+
type: str = "embedding"
|
|
89
|
+
id: str
|
|
90
|
+
attributes: EmbeddingAttributes
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class EmbeddingListResponse(BaseModel):
|
|
94
|
+
"""Embedding list response following JSON-API spec."""
|
|
95
|
+
|
|
96
|
+
data: list[EmbeddingData]
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from typing import TypedDict
|
|
4
4
|
|
|
5
|
+
from kodit.application.factories.server_factory import ServerFactory
|
|
5
6
|
from kodit.config import AppContext
|
|
6
7
|
|
|
7
8
|
|
|
@@ -9,3 +10,4 @@ class AppLifespanState(TypedDict):
|
|
|
9
10
|
"""Application lifespan state."""
|
|
10
11
|
|
|
11
12
|
app_context: AppContext
|
|
13
|
+
server_factory: ServerFactory
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""Repository JSON-API schemas."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from pydantic import AnyUrl, BaseModel
|
|
7
|
+
|
|
8
|
+
from kodit.domain.entities.git import GitRepo
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class RepositoryAttributes(BaseModel):
|
|
12
|
+
"""Repository attributes following JSON-API spec."""
|
|
13
|
+
|
|
14
|
+
remote_uri: AnyUrl
|
|
15
|
+
created_at: datetime | None = None
|
|
16
|
+
updated_at: datetime | None = None
|
|
17
|
+
last_scanned_at: datetime | None = None
|
|
18
|
+
cloned_path: Path | None = None
|
|
19
|
+
tracking_branch: str | None = None
|
|
20
|
+
num_commits: int = 0
|
|
21
|
+
num_branches: int = 0
|
|
22
|
+
num_tags: int = 0
|
|
23
|
+
|
|
24
|
+
@staticmethod
|
|
25
|
+
def from_git_repo(repo: GitRepo) -> "RepositoryAttributes":
|
|
26
|
+
"""Create a repository attributes from a Git repository."""
|
|
27
|
+
return RepositoryAttributes(
|
|
28
|
+
remote_uri=repo.sanitized_remote_uri,
|
|
29
|
+
cloned_path=repo.cloned_path,
|
|
30
|
+
created_at=repo.created_at,
|
|
31
|
+
updated_at=repo.updated_at,
|
|
32
|
+
last_scanned_at=repo.last_scanned_at,
|
|
33
|
+
tracking_branch=repo.tracking_branch.name if repo.tracking_branch else None,
|
|
34
|
+
num_commits=repo.num_commits,
|
|
35
|
+
num_branches=repo.num_branches,
|
|
36
|
+
num_tags=repo.num_tags,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class RepositoryData(BaseModel):
|
|
41
|
+
"""Repository data following JSON-API spec."""
|
|
42
|
+
|
|
43
|
+
type: str = "repository"
|
|
44
|
+
id: str
|
|
45
|
+
attributes: RepositoryAttributes
|
|
46
|
+
|
|
47
|
+
@staticmethod
|
|
48
|
+
def from_git_repo(repo: GitRepo) -> "RepositoryData":
|
|
49
|
+
"""Create a repository data from a Git repository."""
|
|
50
|
+
return RepositoryData(
|
|
51
|
+
id=str(repo.id) or "",
|
|
52
|
+
attributes=RepositoryAttributes.from_git_repo(repo),
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class RepositoryResponse(BaseModel):
|
|
57
|
+
"""Single repository response following JSON-API spec."""
|
|
58
|
+
|
|
59
|
+
data: RepositoryData
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class RepositoryListResponse(BaseModel):
|
|
63
|
+
"""Repository list response following JSON-API spec."""
|
|
64
|
+
|
|
65
|
+
data: list[RepositoryData]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class RepositoryCreateAttributes(BaseModel):
|
|
69
|
+
"""Repository creation attributes."""
|
|
70
|
+
|
|
71
|
+
remote_uri: AnyUrl
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class RepositoryCreateData(BaseModel):
|
|
75
|
+
"""Repository creation data."""
|
|
76
|
+
|
|
77
|
+
type: str = "repository"
|
|
78
|
+
attributes: RepositoryCreateAttributes
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class RepositoryCreateRequest(BaseModel):
|
|
82
|
+
"""Repository creation request."""
|
|
83
|
+
|
|
84
|
+
data: RepositoryCreateData
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class RepositoryUpdateAttributes(BaseModel):
|
|
88
|
+
"""Repository update attributes."""
|
|
89
|
+
|
|
90
|
+
pull_latest: bool = False
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class RepositoryUpdateData(BaseModel):
|
|
94
|
+
"""Repository update data."""
|
|
95
|
+
|
|
96
|
+
type: str = "repository"
|
|
97
|
+
attributes: RepositoryUpdateAttributes
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class RepositoryUpdateRequest(BaseModel):
|
|
101
|
+
"""Repository update request."""
|
|
102
|
+
|
|
103
|
+
data: RepositoryUpdateData
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class RepositoryBranchData(BaseModel):
|
|
107
|
+
"""Repository branch data."""
|
|
108
|
+
|
|
109
|
+
name: str
|
|
110
|
+
is_default: bool
|
|
111
|
+
commit_count: int
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class RepositoryCommitData(BaseModel):
|
|
115
|
+
"""Repository commit data for repository details."""
|
|
116
|
+
|
|
117
|
+
sha: str
|
|
118
|
+
message: str
|
|
119
|
+
author: str
|
|
120
|
+
timestamp: datetime
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class RepositoryDetailsResponse(BaseModel):
|
|
124
|
+
"""Repository details response with branches and commits."""
|
|
125
|
+
|
|
126
|
+
data: RepositoryData
|
|
127
|
+
branches: list[RepositoryBranchData]
|
|
128
|
+
recent_commits: list[RepositoryCommitData]
|
|
@@ -4,6 +4,12 @@ from datetime import datetime
|
|
|
4
4
|
|
|
5
5
|
from pydantic import BaseModel, Field
|
|
6
6
|
|
|
7
|
+
from kodit.infrastructure.api.v1.schemas.snippet import (
|
|
8
|
+
EnrichmentSchema,
|
|
9
|
+
GitFileSchema,
|
|
10
|
+
SnippetContentSchema,
|
|
11
|
+
)
|
|
12
|
+
|
|
7
13
|
|
|
8
14
|
class SearchFilters(BaseModel):
|
|
9
15
|
"""Search filters for JSON:API requests."""
|
|
@@ -111,22 +117,19 @@ class SearchRequest(BaseModel):
|
|
|
111
117
|
class SnippetAttributes(BaseModel):
|
|
112
118
|
"""Snippet attributes for JSON:API responses."""
|
|
113
119
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
120
|
+
created_at: datetime | None = None
|
|
121
|
+
updated_at: datetime | None = None
|
|
122
|
+
derives_from: list[GitFileSchema]
|
|
123
|
+
content: SnippetContentSchema
|
|
124
|
+
enrichments: list[EnrichmentSchema]
|
|
117
125
|
original_scores: list[float]
|
|
118
|
-
source_uri: str
|
|
119
|
-
relative_path: str
|
|
120
|
-
language: str
|
|
121
|
-
authors: list[str]
|
|
122
|
-
summary: str
|
|
123
126
|
|
|
124
127
|
|
|
125
128
|
class SnippetData(BaseModel):
|
|
126
129
|
"""Snippet data for JSON:API responses."""
|
|
127
130
|
|
|
128
131
|
type: str = "snippet"
|
|
129
|
-
id:
|
|
132
|
+
id: str
|
|
130
133
|
attributes: SnippetAttributes
|
|
131
134
|
|
|
132
135
|
|