kodit 0.5.3__py3-none-any.whl → 0.5.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of kodit might be problematic. Click here for more details.
- kodit/_version.py +2 -2
- kodit/application/factories/server_factory.py +54 -32
- kodit/application/services/code_search_application_service.py +89 -12
- kodit/application/services/commit_indexing_application_service.py +314 -195
- kodit/application/services/enrichment_query_service.py +274 -43
- kodit/application/services/indexing_worker_service.py +1 -1
- kodit/application/services/queue_service.py +15 -10
- kodit/application/services/sync_scheduler.py +2 -1
- kodit/domain/enrichments/architecture/architecture.py +1 -1
- kodit/domain/enrichments/architecture/physical/physical.py +1 -1
- kodit/domain/enrichments/development/development.py +1 -1
- kodit/domain/enrichments/development/snippet/snippet.py +12 -5
- kodit/domain/enrichments/enrichment.py +31 -4
- kodit/domain/enrichments/usage/api_docs.py +1 -1
- kodit/domain/enrichments/usage/usage.py +1 -1
- kodit/domain/entities/git.py +30 -25
- kodit/domain/factories/git_repo_factory.py +20 -5
- kodit/domain/protocols.py +56 -125
- kodit/domain/services/embedding_service.py +14 -16
- kodit/domain/services/git_repository_service.py +60 -38
- kodit/domain/services/git_service.py +18 -11
- kodit/domain/tracking/resolution_service.py +6 -16
- kodit/domain/value_objects.py +2 -9
- kodit/infrastructure/api/v1/dependencies.py +12 -3
- kodit/infrastructure/api/v1/query_params.py +27 -0
- kodit/infrastructure/api/v1/routers/commits.py +91 -85
- kodit/infrastructure/api/v1/routers/repositories.py +53 -37
- kodit/infrastructure/api/v1/routers/search.py +1 -1
- kodit/infrastructure/api/v1/schemas/enrichment.py +14 -0
- kodit/infrastructure/api/v1/schemas/repository.py +1 -1
- kodit/infrastructure/providers/litellm_provider.py +23 -1
- kodit/infrastructure/slicing/api_doc_extractor.py +0 -2
- kodit/infrastructure/sqlalchemy/embedding_repository.py +44 -34
- kodit/infrastructure/sqlalchemy/enrichment_association_repository.py +73 -0
- kodit/infrastructure/sqlalchemy/enrichment_v2_repository.py +116 -97
- kodit/infrastructure/sqlalchemy/entities.py +12 -116
- kodit/infrastructure/sqlalchemy/git_branch_repository.py +52 -244
- kodit/infrastructure/sqlalchemy/git_commit_repository.py +35 -324
- kodit/infrastructure/sqlalchemy/git_file_repository.py +70 -0
- kodit/infrastructure/sqlalchemy/git_repository.py +60 -230
- kodit/infrastructure/sqlalchemy/git_tag_repository.py +53 -240
- kodit/infrastructure/sqlalchemy/query.py +331 -0
- kodit/infrastructure/sqlalchemy/repository.py +203 -0
- kodit/infrastructure/sqlalchemy/task_repository.py +79 -58
- kodit/infrastructure/sqlalchemy/task_status_repository.py +45 -52
- kodit/migrations/versions/4b1a3b2c8fa5_refactor_git_tracking.py +190 -0
- {kodit-0.5.3.dist-info → kodit-0.5.5.dist-info}/METADATA +1 -1
- {kodit-0.5.3.dist-info → kodit-0.5.5.dist-info}/RECORD +51 -49
- kodit/infrastructure/mappers/enrichment_mapper.py +0 -83
- kodit/infrastructure/mappers/git_mapper.py +0 -193
- kodit/infrastructure/mappers/snippet_mapper.py +0 -104
- kodit/infrastructure/sqlalchemy/snippet_v2_repository.py +0 -479
- {kodit-0.5.3.dist-info → kodit-0.5.5.dist-info}/WHEEL +0 -0
- {kodit-0.5.3.dist-info → kodit-0.5.5.dist-info}/entry_points.txt +0 -0
- {kodit-0.5.3.dist-info → kodit-0.5.5.dist-info}/licenses/LICENSE +0 -0
|
@@ -3,12 +3,20 @@
|
|
|
3
3
|
from typing import Annotated
|
|
4
4
|
|
|
5
5
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
6
|
+
from fastapi.responses import RedirectResponse
|
|
6
7
|
|
|
8
|
+
from kodit.domain.enrichments.development.development import ENRICHMENT_TYPE_DEVELOPMENT
|
|
9
|
+
from kodit.domain.enrichments.development.snippet.snippet import (
|
|
10
|
+
ENRICHMENT_SUBTYPE_SNIPPET,
|
|
11
|
+
)
|
|
12
|
+
from kodit.domain.entities.git import GitFile
|
|
7
13
|
from kodit.infrastructure.api.middleware.auth import api_key_auth
|
|
8
14
|
from kodit.infrastructure.api.v1.dependencies import (
|
|
9
15
|
GitCommitRepositoryDep,
|
|
16
|
+
GitFileRepositoryDep,
|
|
10
17
|
ServerFactoryDep,
|
|
11
18
|
)
|
|
19
|
+
from kodit.infrastructure.api.v1.query_params import PaginationParamsDep
|
|
12
20
|
from kodit.infrastructure.api.v1.schemas.commit import (
|
|
13
21
|
CommitAttributes,
|
|
14
22
|
CommitData,
|
|
@@ -23,17 +31,18 @@ from kodit.infrastructure.api.v1.schemas.commit import (
|
|
|
23
31
|
FileResponse,
|
|
24
32
|
)
|
|
25
33
|
from kodit.infrastructure.api.v1.schemas.enrichment import (
|
|
34
|
+
EnrichmentAssociationData,
|
|
26
35
|
EnrichmentAttributes,
|
|
27
36
|
EnrichmentData,
|
|
28
37
|
EnrichmentListResponse,
|
|
38
|
+
EnrichmentRelationships,
|
|
29
39
|
)
|
|
30
|
-
from kodit.infrastructure.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
SnippetListResponse,
|
|
40
|
+
from kodit.infrastructure.sqlalchemy.query import (
|
|
41
|
+
EnrichmentAssociationQueryBuilder,
|
|
42
|
+
EnrichmentQueryBuilder,
|
|
43
|
+
FilterOperator,
|
|
44
|
+
GitFileQueryBuilder,
|
|
45
|
+
QueryBuilder,
|
|
37
46
|
)
|
|
38
47
|
|
|
39
48
|
router = APIRouter(
|
|
@@ -49,12 +58,19 @@ router = APIRouter(
|
|
|
49
58
|
|
|
50
59
|
@router.get("/{repo_id}/commits", summary="List repository commits")
|
|
51
60
|
async def list_repository_commits(
|
|
52
|
-
repo_id: str,
|
|
61
|
+
repo_id: str,
|
|
62
|
+
git_commit_repository: GitCommitRepositoryDep,
|
|
63
|
+
pagination_params: PaginationParamsDep,
|
|
53
64
|
) -> CommitListResponse:
|
|
54
65
|
"""List all commits for a repository."""
|
|
55
66
|
try:
|
|
56
67
|
# Get all commits for the repository directly from commit repository
|
|
57
|
-
commits = await git_commit_repository.
|
|
68
|
+
commits = await git_commit_repository.find(
|
|
69
|
+
QueryBuilder()
|
|
70
|
+
.filter("repo_id", FilterOperator.EQ, int(repo_id))
|
|
71
|
+
.paginate(pagination_params)
|
|
72
|
+
.sort("date", descending=True)
|
|
73
|
+
)
|
|
58
74
|
except ValueError as e:
|
|
59
75
|
raise HTTPException(status_code=404, detail="Repository not found") from e
|
|
60
76
|
|
|
@@ -89,7 +105,7 @@ async def get_repository_commit(
|
|
|
89
105
|
"""Get a specific commit for a repository."""
|
|
90
106
|
try:
|
|
91
107
|
# Get the specific commit directly from commit repository
|
|
92
|
-
commit = await git_commit_repository.
|
|
108
|
+
commit = await git_commit_repository.get(commit_sha)
|
|
93
109
|
except ValueError as e:
|
|
94
110
|
raise HTTPException(status_code=404, detail="Commit not found") from e
|
|
95
111
|
|
|
@@ -112,19 +128,17 @@ async def get_repository_commit(
|
|
|
112
128
|
async def list_commit_files(
|
|
113
129
|
repo_id: str, # noqa: ARG001
|
|
114
130
|
commit_sha: str,
|
|
115
|
-
|
|
131
|
+
git_file_repository: GitFileRepositoryDep,
|
|
132
|
+
pagination: PaginationParamsDep,
|
|
116
133
|
) -> FileListResponse:
|
|
117
134
|
"""List all files in a specific commit."""
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
except ValueError as e:
|
|
122
|
-
raise HTTPException(status_code=404, detail="Commit not found") from e
|
|
123
|
-
|
|
135
|
+
files = await git_file_repository.find(
|
|
136
|
+
GitFileQueryBuilder().for_commit_sha(commit_sha).paginate(pagination)
|
|
137
|
+
)
|
|
124
138
|
return FileListResponse(
|
|
125
139
|
data=[
|
|
126
140
|
FileData(
|
|
127
|
-
type=
|
|
141
|
+
type=GitFile.__name__,
|
|
128
142
|
id=file.blob_sha,
|
|
129
143
|
attributes=FileAttributes(
|
|
130
144
|
blob_sha=file.blob_sha,
|
|
@@ -134,7 +148,7 @@ async def list_commit_files(
|
|
|
134
148
|
extension=file.extension,
|
|
135
149
|
),
|
|
136
150
|
)
|
|
137
|
-
for file in
|
|
151
|
+
for file in files
|
|
138
152
|
]
|
|
139
153
|
)
|
|
140
154
|
|
|
@@ -148,20 +162,17 @@ async def get_commit_file(
|
|
|
148
162
|
repo_id: str, # noqa: ARG001
|
|
149
163
|
commit_sha: str,
|
|
150
164
|
blob_sha: str,
|
|
151
|
-
|
|
165
|
+
git_file_repository: GitFileRepositoryDep,
|
|
152
166
|
) -> FileResponse:
|
|
153
167
|
"""Get a specific file from a commit."""
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
raise HTTPException(status_code=404, detail="Commit not found") from e
|
|
159
|
-
|
|
160
|
-
# Find the specific file
|
|
161
|
-
file = next((f for f in commit.files if f.blob_sha == blob_sha), None)
|
|
162
|
-
if not file:
|
|
168
|
+
files = await git_file_repository.find(
|
|
169
|
+
GitFileQueryBuilder().for_commit_sha(commit_sha).for_blob_sha(blob_sha)
|
|
170
|
+
)
|
|
171
|
+
if not files:
|
|
163
172
|
raise HTTPException(status_code=404, detail="File not found")
|
|
164
|
-
|
|
173
|
+
if len(files) > 1:
|
|
174
|
+
raise HTTPException(status_code=422, detail="Multiple files found")
|
|
175
|
+
file = files[0]
|
|
165
176
|
return FileResponse(
|
|
166
177
|
data=FileData(
|
|
167
178
|
type="file",
|
|
@@ -185,45 +196,11 @@ async def get_commit_file(
|
|
|
185
196
|
async def list_commit_snippets(
|
|
186
197
|
repo_id: str,
|
|
187
198
|
commit_sha: str,
|
|
188
|
-
|
|
189
|
-
) -> SnippetListResponse:
|
|
199
|
+
) -> RedirectResponse:
|
|
190
200
|
"""List all snippets in a specific commit."""
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
return SnippetListResponse(
|
|
196
|
-
data=[
|
|
197
|
-
SnippetData(
|
|
198
|
-
type="snippet",
|
|
199
|
-
id=snippet.sha,
|
|
200
|
-
attributes=SnippetAttributes(
|
|
201
|
-
created_at=snippet.created_at,
|
|
202
|
-
updated_at=snippet.updated_at,
|
|
203
|
-
derives_from=[
|
|
204
|
-
GitFileSchema(
|
|
205
|
-
blob_sha=file.blob_sha,
|
|
206
|
-
path=file.path,
|
|
207
|
-
mime_type=file.mime_type,
|
|
208
|
-
size=file.size,
|
|
209
|
-
)
|
|
210
|
-
for file in snippet.derives_from
|
|
211
|
-
],
|
|
212
|
-
content=SnippetContentSchema(
|
|
213
|
-
value=snippet.content,
|
|
214
|
-
language=snippet.extension,
|
|
215
|
-
),
|
|
216
|
-
enrichments=[
|
|
217
|
-
EnrichmentSchema(
|
|
218
|
-
type=enrichment.type.value,
|
|
219
|
-
content=enrichment.content,
|
|
220
|
-
)
|
|
221
|
-
for enrichment in snippet.enrichments
|
|
222
|
-
],
|
|
223
|
-
),
|
|
224
|
-
)
|
|
225
|
-
for snippet in snippets
|
|
226
|
-
]
|
|
201
|
+
return RedirectResponse(
|
|
202
|
+
status_code=308,
|
|
203
|
+
url=f"/api/v1/repositories/{repo_id}/commits/{commit_sha}/enrichments?enrichment_type={ENRICHMENT_TYPE_DEVELOPMENT}&enrichment_subtype={ENRICHMENT_SUBTYPE_SNIPPET}",
|
|
227
204
|
)
|
|
228
205
|
|
|
229
206
|
|
|
@@ -247,14 +224,12 @@ async def list_commit_embeddings(
|
|
|
247
224
|
) -> EmbeddingListResponse:
|
|
248
225
|
"""List all embeddings for snippets in a specific commit."""
|
|
249
226
|
_ = repo_id # Required by FastAPI route path but not used in function
|
|
250
|
-
snippet_repository = server_factory.snippet_v2_repository()
|
|
251
|
-
snippets = await snippet_repository.get_snippets_for_commit(commit_sha)
|
|
252
227
|
|
|
253
|
-
|
|
254
|
-
|
|
228
|
+
enrichment_query_service = server_factory.enrichment_query_service()
|
|
229
|
+
snippets = await enrichment_query_service.get_all_snippets_for_commit(commit_sha)
|
|
255
230
|
|
|
256
231
|
# Get snippet SHAs
|
|
257
|
-
snippet_shas = [snippet.
|
|
232
|
+
snippet_shas = [str(snippet.id) for snippet in snippets]
|
|
258
233
|
|
|
259
234
|
# Get embeddings for all snippets in the commit
|
|
260
235
|
embedding_repository = server_factory.embedding_repository()
|
|
@@ -285,15 +260,18 @@ async def list_commit_enrichments(
|
|
|
285
260
|
repo_id: str, # noqa: ARG001
|
|
286
261
|
commit_sha: str,
|
|
287
262
|
server_factory: ServerFactoryDep,
|
|
263
|
+
pagination_params: PaginationParamsDep,
|
|
264
|
+
enrichment_type: str | None = None,
|
|
288
265
|
) -> EnrichmentListResponse:
|
|
289
266
|
"""List all enrichments for a specific commit."""
|
|
290
267
|
# TODO(Phil): Should use repo too, it's confusing to the user when they specify the
|
|
291
268
|
# wrong commit and another repo. It's like they are seeing results from the other
|
|
292
269
|
# repo.
|
|
293
|
-
|
|
294
|
-
enrichments = await
|
|
295
|
-
|
|
296
|
-
|
|
270
|
+
enrichment_query_service = server_factory.enrichment_query_service()
|
|
271
|
+
enrichments = await enrichment_query_service.all_enrichments_for_commit(
|
|
272
|
+
commit_sha=commit_sha,
|
|
273
|
+
pagination=pagination_params,
|
|
274
|
+
enrichment_type=enrichment_type,
|
|
297
275
|
)
|
|
298
276
|
|
|
299
277
|
return EnrichmentListResponse(
|
|
@@ -308,8 +286,17 @@ async def list_commit_enrichments(
|
|
|
308
286
|
created_at=enrichment.created_at,
|
|
309
287
|
updated_at=enrichment.updated_at,
|
|
310
288
|
),
|
|
289
|
+
relationships=EnrichmentRelationships(
|
|
290
|
+
associations=[
|
|
291
|
+
EnrichmentAssociationData(
|
|
292
|
+
id=association.entity_id,
|
|
293
|
+
type=association.entity_type,
|
|
294
|
+
)
|
|
295
|
+
for association in associations
|
|
296
|
+
],
|
|
297
|
+
),
|
|
311
298
|
)
|
|
312
|
-
for enrichment in enrichments
|
|
299
|
+
for enrichment, associations in enrichments.items()
|
|
313
300
|
]
|
|
314
301
|
)
|
|
315
302
|
|
|
@@ -327,9 +314,26 @@ async def delete_all_commit_enrichments(
|
|
|
327
314
|
) -> None:
|
|
328
315
|
"""Delete all enrichments for a specific commit."""
|
|
329
316
|
enrichment_v2_repository = server_factory.enrichment_v2_repository()
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
317
|
+
enrichment_association_repository = (
|
|
318
|
+
server_factory.enrichment_association_repository()
|
|
319
|
+
)
|
|
320
|
+
associations = await enrichment_association_repository.find(
|
|
321
|
+
EnrichmentAssociationQueryBuilder().for_commit(commit_sha)
|
|
322
|
+
)
|
|
323
|
+
enrichments = await enrichment_v2_repository.find(
|
|
324
|
+
EnrichmentQueryBuilder().for_ids(
|
|
325
|
+
enrichment_ids=[association.enrichment_id for association in associations]
|
|
326
|
+
)
|
|
327
|
+
)
|
|
328
|
+
await enrichment_association_repository.delete_by_query(
|
|
329
|
+
EnrichmentAssociationQueryBuilder().for_enrichments(enrichments)
|
|
330
|
+
)
|
|
331
|
+
await enrichment_v2_repository.delete_by_query(
|
|
332
|
+
EnrichmentQueryBuilder().for_ids(
|
|
333
|
+
enrichment_ids=[
|
|
334
|
+
enrichment.id for enrichment in enrichments if enrichment.id
|
|
335
|
+
]
|
|
336
|
+
)
|
|
333
337
|
)
|
|
334
338
|
|
|
335
339
|
|
|
@@ -346,7 +350,9 @@ async def delete_commit_enrichment(
|
|
|
346
350
|
server_factory: ServerFactoryDep,
|
|
347
351
|
) -> None:
|
|
348
352
|
"""Delete a specific enrichment for a commit."""
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
+
try:
|
|
354
|
+
enrichment_v2_repository = server_factory.enrichment_v2_repository()
|
|
355
|
+
enrichment = await enrichment_v2_repository.get(enrichment_id)
|
|
356
|
+
await enrichment_v2_repository.delete(enrichment)
|
|
357
|
+
except ValueError as e:
|
|
358
|
+
raise HTTPException(status_code=404, detail="Enrichment not found") from e
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"""Repository management router for the REST API."""
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from typing import Annotated
|
|
4
|
+
|
|
5
|
+
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
4
6
|
|
|
5
7
|
from kodit.domain.tracking.trackable import Trackable, TrackableReferenceType
|
|
6
8
|
from kodit.infrastructure.api.middleware.auth import api_key_auth
|
|
@@ -13,6 +15,7 @@ from kodit.infrastructure.api.v1.dependencies import (
|
|
|
13
15
|
GitTagRepositoryDep,
|
|
14
16
|
TaskStatusQueryServiceDep,
|
|
15
17
|
)
|
|
18
|
+
from kodit.infrastructure.api.v1.query_params import PaginationParamsDep
|
|
16
19
|
from kodit.infrastructure.api.v1.schemas.enrichment import (
|
|
17
20
|
EnrichmentAttributes,
|
|
18
21
|
EnrichmentData,
|
|
@@ -38,6 +41,7 @@ from kodit.infrastructure.api.v1.schemas.task_status import (
|
|
|
38
41
|
TaskStatusData,
|
|
39
42
|
TaskStatusListResponse,
|
|
40
43
|
)
|
|
44
|
+
from kodit.infrastructure.sqlalchemy.query import FilterOperator, QueryBuilder
|
|
41
45
|
|
|
42
46
|
router = APIRouter(
|
|
43
47
|
prefix="/api/v1/repositories",
|
|
@@ -60,7 +64,7 @@ async def list_repositories(
|
|
|
60
64
|
git_repository: GitRepositoryDep,
|
|
61
65
|
) -> RepositoryListResponse:
|
|
62
66
|
"""List all cloned repositories."""
|
|
63
|
-
repos = await git_repository.
|
|
67
|
+
repos = await git_repository.find(QueryBuilder())
|
|
64
68
|
return RepositoryListResponse(
|
|
65
69
|
data=[RepositoryData.from_git_repo(repo) for repo in repos]
|
|
66
70
|
)
|
|
@@ -99,36 +103,47 @@ async def get_repository(
|
|
|
99
103
|
git_branch_repository: GitBranchRepositoryDep,
|
|
100
104
|
) -> RepositoryDetailsResponse:
|
|
101
105
|
"""Get repository details including branches and recent commits."""
|
|
102
|
-
repo = await git_repository.
|
|
106
|
+
repo = await git_repository.get(int(repo_id))
|
|
103
107
|
if not repo:
|
|
104
108
|
raise HTTPException(status_code=404, detail="Repository not found")
|
|
105
109
|
|
|
110
|
+
# Get branches for the repository using the branch repository
|
|
111
|
+
repo_branches = await git_branch_repository.find(
|
|
112
|
+
QueryBuilder().filter("repo_id", FilterOperator.EQ, int(repo_id))
|
|
113
|
+
)
|
|
114
|
+
|
|
106
115
|
# Get all commits for this repository from the commit repository
|
|
107
|
-
repo_commits = await git_commit_repository.
|
|
116
|
+
repo_commits = await git_commit_repository.find(
|
|
117
|
+
QueryBuilder().filter("repo_id", FilterOperator.EQ, int(repo_id))
|
|
118
|
+
)
|
|
108
119
|
commits_by_sha = {commit.commit_sha: commit for commit in repo_commits}
|
|
109
120
|
|
|
110
121
|
# Get recent commits from the tracking branch's head commit
|
|
111
122
|
recent_commits = []
|
|
112
|
-
|
|
123
|
+
# Get the tracking branch from the branch repository
|
|
124
|
+
tracking_branch = next(
|
|
125
|
+
(b for b in repo_branches if b.name == repo.tracking_config.name), None
|
|
126
|
+
)
|
|
127
|
+
if tracking_branch and tracking_branch.head_commit_sha:
|
|
113
128
|
# For simplicity, just show the head commit and traverse back if needed
|
|
114
|
-
current_commit =
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
129
|
+
current_commit = commits_by_sha.get(tracking_branch.head_commit_sha)
|
|
130
|
+
if current_commit:
|
|
131
|
+
recent_commits = [current_commit]
|
|
132
|
+
|
|
133
|
+
# Traverse parent commits for more recent commits (up to 10)
|
|
134
|
+
current_sha = current_commit.parent_commit_sha
|
|
135
|
+
while current_sha and len(recent_commits) < 10:
|
|
136
|
+
parent_commit = commits_by_sha.get(current_sha)
|
|
137
|
+
if parent_commit:
|
|
138
|
+
recent_commits.append(parent_commit)
|
|
139
|
+
current_sha = parent_commit.parent_commit_sha
|
|
140
|
+
else:
|
|
141
|
+
break
|
|
126
142
|
|
|
127
143
|
# Get commit count for the repository using the commit repository
|
|
128
|
-
commit_count = await git_commit_repository.
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
repo_branches = await git_branch_repository.get_by_repo_id(int(repo_id))
|
|
144
|
+
commit_count = await git_commit_repository.count(
|
|
145
|
+
QueryBuilder().filter("repo_id", FilterOperator.EQ, int(repo_id))
|
|
146
|
+
)
|
|
132
147
|
|
|
133
148
|
# Get commit counts for all branches using the commit repository
|
|
134
149
|
branch_data = []
|
|
@@ -140,9 +155,7 @@ async def get_repository(
|
|
|
140
155
|
branch_data.append(
|
|
141
156
|
RepositoryBranchData(
|
|
142
157
|
name=branch.name,
|
|
143
|
-
is_default=branch.name == repo.
|
|
144
|
-
if repo.tracking_branch
|
|
145
|
-
else False,
|
|
158
|
+
is_default=branch.name == repo.tracking_config.name,
|
|
146
159
|
commit_count=branch_commit_count,
|
|
147
160
|
)
|
|
148
161
|
)
|
|
@@ -208,7 +221,7 @@ async def list_repository_tags(
|
|
|
208
221
|
git_tag_repository: GitTagRepositoryDep,
|
|
209
222
|
) -> TagListResponse:
|
|
210
223
|
"""List all tags for a repository."""
|
|
211
|
-
repo = await git_repository.
|
|
224
|
+
repo = await git_repository.get(int(repo_id))
|
|
212
225
|
if not repo:
|
|
213
226
|
raise HTTPException(status_code=404, detail="Repository not found")
|
|
214
227
|
|
|
@@ -222,7 +235,7 @@ async def list_repository_tags(
|
|
|
222
235
|
id=tag.id,
|
|
223
236
|
attributes=TagAttributes(
|
|
224
237
|
name=tag.name,
|
|
225
|
-
target_commit_sha=tag.
|
|
238
|
+
target_commit_sha=tag.target_commit_sha,
|
|
226
239
|
is_version_tag=tag.is_version_tag,
|
|
227
240
|
),
|
|
228
241
|
)
|
|
@@ -243,7 +256,7 @@ async def get_repository_tag(
|
|
|
243
256
|
git_tag_repository: GitTagRepositoryDep,
|
|
244
257
|
) -> TagResponse:
|
|
245
258
|
"""Get a specific tag for a repository."""
|
|
246
|
-
repo = await git_repository.
|
|
259
|
+
repo = await git_repository.get(int(repo_id))
|
|
247
260
|
if not repo:
|
|
248
261
|
raise HTTPException(status_code=404, detail="Repository not found")
|
|
249
262
|
|
|
@@ -259,7 +272,7 @@ async def get_repository_tag(
|
|
|
259
272
|
id=tag.id,
|
|
260
273
|
attributes=TagAttributes(
|
|
261
274
|
name=tag.name,
|
|
262
|
-
target_commit_sha=tag.
|
|
275
|
+
target_commit_sha=tag.target_commit_sha,
|
|
263
276
|
is_version_tag=tag.is_version_tag,
|
|
264
277
|
),
|
|
265
278
|
)
|
|
@@ -275,10 +288,16 @@ async def list_repository_enrichments( # noqa: PLR0913
|
|
|
275
288
|
repo_id: str,
|
|
276
289
|
git_repository: GitRepositoryDep,
|
|
277
290
|
enrichment_query_service: EnrichmentQueryServiceDep,
|
|
291
|
+
pagination: PaginationParamsDep,
|
|
278
292
|
ref_type: str = "branch",
|
|
279
293
|
ref_name: str | None = None,
|
|
280
294
|
enrichment_type: str | None = None,
|
|
281
|
-
|
|
295
|
+
max_commits_to_check: Annotated[
|
|
296
|
+
int,
|
|
297
|
+
Query(
|
|
298
|
+
description="Number of recent commits to search for recent enriched commits"
|
|
299
|
+
),
|
|
300
|
+
] = 10,
|
|
282
301
|
) -> EnrichmentListResponse:
|
|
283
302
|
"""List the most recent enrichments for a repository.
|
|
284
303
|
|
|
@@ -289,7 +308,7 @@ async def list_repository_enrichments( # noqa: PLR0913
|
|
|
289
308
|
- limit: Maximum number of enrichments to return. Defaults to 10.
|
|
290
309
|
"""
|
|
291
310
|
# Get repository
|
|
292
|
-
repo = await git_repository.
|
|
311
|
+
repo = await git_repository.get(int(repo_id))
|
|
293
312
|
if not repo:
|
|
294
313
|
raise HTTPException(status_code=404, detail="Repository not found")
|
|
295
314
|
|
|
@@ -297,11 +316,7 @@ async def list_repository_enrichments( # noqa: PLR0913
|
|
|
297
316
|
if ref_name is None:
|
|
298
317
|
if ref_type == "branch":
|
|
299
318
|
# Default to tracking branch
|
|
300
|
-
|
|
301
|
-
raise HTTPException(
|
|
302
|
-
status_code=400, detail="No tracking branch configured"
|
|
303
|
-
)
|
|
304
|
-
ref_name = repo.tracking_branch.name
|
|
319
|
+
ref_name = repo.tracking_config.name
|
|
305
320
|
else:
|
|
306
321
|
raise HTTPException(
|
|
307
322
|
status_code=400,
|
|
@@ -326,7 +341,7 @@ async def list_repository_enrichments( # noqa: PLR0913
|
|
|
326
341
|
enriched_commit = await enrichment_query_service.find_latest_enriched_commit(
|
|
327
342
|
trackable=trackable,
|
|
328
343
|
enrichment_type=enrichment_type,
|
|
329
|
-
max_commits_to_check=
|
|
344
|
+
max_commits_to_check=max_commits_to_check,
|
|
330
345
|
)
|
|
331
346
|
|
|
332
347
|
# If no enriched commit found, return empty list
|
|
@@ -334,9 +349,10 @@ async def list_repository_enrichments( # noqa: PLR0913
|
|
|
334
349
|
return EnrichmentListResponse(data=[])
|
|
335
350
|
|
|
336
351
|
# Get enrichments for the commit
|
|
337
|
-
enrichments = await enrichment_query_service.
|
|
352
|
+
enrichments = await enrichment_query_service.all_enrichments_for_commit(
|
|
338
353
|
commit_sha=enriched_commit,
|
|
339
354
|
enrichment_type=enrichment_type,
|
|
355
|
+
pagination=pagination,
|
|
340
356
|
)
|
|
341
357
|
|
|
342
358
|
# Map enrichments to API response format
|
|
@@ -15,12 +15,26 @@ class EnrichmentAttributes(BaseModel):
|
|
|
15
15
|
updated_at: datetime | None
|
|
16
16
|
|
|
17
17
|
|
|
18
|
+
class EnrichmentAssociationData(BaseModel):
|
|
19
|
+
"""Enrichment association data for JSON-API spec."""
|
|
20
|
+
|
|
21
|
+
id: str
|
|
22
|
+
type: str
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class EnrichmentRelationships(BaseModel):
|
|
26
|
+
"""Enrichment relationships for JSON-API spec."""
|
|
27
|
+
|
|
28
|
+
associations: list[EnrichmentAssociationData] | None = None
|
|
29
|
+
|
|
30
|
+
|
|
18
31
|
class EnrichmentData(BaseModel):
|
|
19
32
|
"""Enrichment data following JSON-API spec."""
|
|
20
33
|
|
|
21
34
|
type: str = "enrichment"
|
|
22
35
|
id: str
|
|
23
36
|
attributes: EnrichmentAttributes
|
|
37
|
+
relationships: EnrichmentRelationships | None = None
|
|
24
38
|
|
|
25
39
|
|
|
26
40
|
class EnrichmentListResponse(BaseModel):
|
|
@@ -30,7 +30,7 @@ class RepositoryAttributes(BaseModel):
|
|
|
30
30
|
created_at=repo.created_at,
|
|
31
31
|
updated_at=repo.updated_at,
|
|
32
32
|
last_scanned_at=repo.last_scanned_at,
|
|
33
|
-
tracking_branch=repo.
|
|
33
|
+
tracking_branch=repo.tracking_config.name,
|
|
34
34
|
num_commits=repo.num_commits,
|
|
35
35
|
num_branches=repo.num_branches,
|
|
36
36
|
num_tags=repo.num_tags,
|
|
@@ -5,6 +5,7 @@ import functools
|
|
|
5
5
|
from collections.abc import Callable, Coroutine
|
|
6
6
|
from typing import Any
|
|
7
7
|
|
|
8
|
+
import httpx
|
|
8
9
|
import litellm
|
|
9
10
|
import structlog
|
|
10
11
|
from litellm import acompletion, aembedding
|
|
@@ -94,6 +95,20 @@ class LiteLLMProvider:
|
|
|
94
95
|
def __init__(self, endpoint: Endpoint) -> None:
|
|
95
96
|
"""Initialize the LiteLLM provider."""
|
|
96
97
|
self.endpoint = endpoint
|
|
98
|
+
self._setup_litellm_client()
|
|
99
|
+
|
|
100
|
+
def _setup_litellm_client(self) -> None:
|
|
101
|
+
"""Set up LiteLLM with custom HTTPX client for Unix socket support."""
|
|
102
|
+
if self.endpoint.socket_path:
|
|
103
|
+
# Create HTTPX client with Unix socket transport
|
|
104
|
+
transport = httpx.AsyncHTTPTransport(uds=self.endpoint.socket_path)
|
|
105
|
+
unix_client = httpx.AsyncClient(
|
|
106
|
+
transport=transport,
|
|
107
|
+
base_url="http://localhost", # Base URL for Unix socket
|
|
108
|
+
timeout=self.endpoint.timeout,
|
|
109
|
+
)
|
|
110
|
+
# Set as LiteLLM's async client session
|
|
111
|
+
litellm.aclient_session = unix_client
|
|
97
112
|
|
|
98
113
|
def _populate_base_kwargs(self) -> dict[str, Any]:
|
|
99
114
|
"""Populate base kwargs common to all API calls."""
|
|
@@ -129,4 +144,11 @@ class LiteLLMProvider:
|
|
|
129
144
|
return response.model_dump()
|
|
130
145
|
|
|
131
146
|
async def close(self) -> None:
|
|
132
|
-
"""Close the provider
|
|
147
|
+
"""Close the provider and cleanup HTTPX client if using Unix sockets."""
|
|
148
|
+
if (
|
|
149
|
+
self.endpoint.socket_path
|
|
150
|
+
and hasattr(litellm, "aclient_session")
|
|
151
|
+
and litellm.aclient_session
|
|
152
|
+
):
|
|
153
|
+
await litellm.aclient_session.aclose()
|
|
154
|
+
litellm.aclient_session = None
|
|
@@ -39,7 +39,6 @@ class APIDocExtractor:
|
|
|
39
39
|
self,
|
|
40
40
|
files: list[GitFile],
|
|
41
41
|
language: str,
|
|
42
|
-
commit_sha: str,
|
|
43
42
|
include_private: bool = False, # noqa: FBT001, FBT002
|
|
44
43
|
) -> list[APIDocEnrichment]:
|
|
45
44
|
"""Extract API documentation enrichments from files.
|
|
@@ -93,7 +92,6 @@ class APIDocExtractor:
|
|
|
93
92
|
)
|
|
94
93
|
|
|
95
94
|
enrichment = APIDocEnrichment(
|
|
96
|
-
entity_id=commit_sha,
|
|
97
95
|
language=language,
|
|
98
96
|
content=markdown_content,
|
|
99
97
|
)
|