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
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
"""Queue service for managing tasks."""
|
|
2
2
|
|
|
3
3
|
from collections.abc import Callable
|
|
4
|
+
from typing import Any
|
|
4
5
|
|
|
5
6
|
import structlog
|
|
6
7
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
7
8
|
|
|
8
9
|
from kodit.domain.entities import Task
|
|
9
|
-
from kodit.domain.value_objects import
|
|
10
|
+
from kodit.domain.value_objects import QueuePriority, TaskOperation
|
|
10
11
|
from kodit.infrastructure.sqlalchemy.task_repository import (
|
|
11
12
|
create_task_repository,
|
|
12
13
|
)
|
|
@@ -46,9 +47,29 @@ class QueueService:
|
|
|
46
47
|
payload=task.payload,
|
|
47
48
|
)
|
|
48
49
|
|
|
49
|
-
async def
|
|
50
|
+
async def enqueue_tasks(
|
|
51
|
+
self,
|
|
52
|
+
tasks: list[TaskOperation],
|
|
53
|
+
base_priority: QueuePriority,
|
|
54
|
+
payload: dict[str, Any],
|
|
55
|
+
) -> None:
|
|
56
|
+
"""Queue repository tasks."""
|
|
57
|
+
priority_offset = len(tasks) * 10
|
|
58
|
+
for task in tasks:
|
|
59
|
+
await self.enqueue_task(
|
|
60
|
+
Task.create(
|
|
61
|
+
task,
|
|
62
|
+
base_priority + priority_offset,
|
|
63
|
+
payload,
|
|
64
|
+
)
|
|
65
|
+
)
|
|
66
|
+
priority_offset -= 10
|
|
67
|
+
|
|
68
|
+
async def list_tasks(
|
|
69
|
+
self, task_operation: TaskOperation | None = None
|
|
70
|
+
) -> list[Task]:
|
|
50
71
|
"""List all tasks in the queue."""
|
|
51
|
-
return await self.task_repository.list(
|
|
72
|
+
return await self.task_repository.list(task_operation)
|
|
52
73
|
|
|
53
74
|
async def get_task(self, task_id: str) -> Task | None:
|
|
54
75
|
"""Get a specific task by ID."""
|
|
@@ -1,86 +1,102 @@
|
|
|
1
1
|
"""Reporting."""
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
from
|
|
3
|
+
from collections.abc import AsyncGenerator
|
|
4
|
+
from contextlib import asynccontextmanager
|
|
5
5
|
from typing import TYPE_CHECKING
|
|
6
6
|
|
|
7
7
|
import structlog
|
|
8
8
|
|
|
9
|
-
from kodit.domain.
|
|
9
|
+
from kodit.domain.entities import TaskStatus
|
|
10
|
+
from kodit.domain.value_objects import TaskOperation, TrackableType
|
|
10
11
|
|
|
11
12
|
if TYPE_CHECKING:
|
|
12
13
|
from kodit.domain.protocols import ReportingModule
|
|
13
14
|
|
|
14
15
|
|
|
15
|
-
class
|
|
16
|
-
"""
|
|
17
|
-
|
|
18
|
-
ROOT = "kodit.root"
|
|
19
|
-
CREATE_INDEX = "kodit.index.create"
|
|
20
|
-
RUN_INDEX = "kodit.index.run"
|
|
16
|
+
class ProgressTracker:
|
|
17
|
+
"""Progress tracker.
|
|
21
18
|
|
|
19
|
+
Provides a reactive wrapper around TaskStatus domain entities that automatically
|
|
20
|
+
propagates state changes to the database and reporting modules. This pattern was
|
|
21
|
+
chosen over a traditional service-repository approach because:
|
|
22
|
+
- State changes must trigger immediate side effects (database writes, notifications)
|
|
23
|
+
- Multiple consumers need real-time updates without polling
|
|
24
|
+
- The wrapper pattern allows transparent interception of all state mutations
|
|
22
25
|
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
The tracker monitors all modifications to the underlying TaskStatus and ensures
|
|
27
|
+
consistency across all downstream systems.
|
|
28
|
+
"""
|
|
25
29
|
|
|
26
|
-
def __init__(
|
|
30
|
+
def __init__(
|
|
31
|
+
self,
|
|
32
|
+
task_status: TaskStatus,
|
|
33
|
+
) -> None:
|
|
27
34
|
"""Initialize the progress tracker."""
|
|
28
|
-
self.
|
|
29
|
-
self._children: list[ProgressTracker] = []
|
|
35
|
+
self.task_status = task_status
|
|
30
36
|
self._log = structlog.get_logger(__name__)
|
|
31
37
|
self._subscribers: list[ReportingModule] = []
|
|
32
|
-
self._snapshot: Progress = Progress(name=name, state=ReportingState.IN_PROGRESS)
|
|
33
|
-
|
|
34
|
-
def __enter__(self) -> "ProgressTracker":
|
|
35
|
-
"""Enter the operation."""
|
|
36
|
-
self._notify_subscribers()
|
|
37
|
-
return self
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
39
|
+
@staticmethod
|
|
40
|
+
def create(
|
|
41
|
+
operation: TaskOperation,
|
|
42
|
+
parent: "TaskStatus | None" = None,
|
|
43
|
+
trackable_type: TrackableType | None = None,
|
|
44
|
+
trackable_id: int | None = None,
|
|
45
|
+
) -> "ProgressTracker":
|
|
46
|
+
"""Create a progress tracker."""
|
|
47
|
+
return ProgressTracker(
|
|
48
|
+
TaskStatus.create(
|
|
49
|
+
operation=operation,
|
|
50
|
+
trackable_type=trackable_type,
|
|
51
|
+
trackable_id=trackable_id,
|
|
52
|
+
parent=parent,
|
|
50
53
|
)
|
|
54
|
+
)
|
|
51
55
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
@asynccontextmanager
|
|
57
|
+
async def create_child(
|
|
58
|
+
self,
|
|
59
|
+
operation: TaskOperation,
|
|
60
|
+
trackable_type: TrackableType | None = None,
|
|
61
|
+
trackable_id: int | None = None,
|
|
62
|
+
) -> AsyncGenerator["ProgressTracker", None]:
|
|
58
63
|
"""Create a child step."""
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
c = ProgressTracker.create(
|
|
65
|
+
operation=operation,
|
|
66
|
+
parent=self.task_status,
|
|
67
|
+
trackable_type=trackable_type or self.task_status.trackable_type,
|
|
68
|
+
trackable_id=trackable_id or self.task_status.trackable_id,
|
|
69
|
+
)
|
|
70
|
+
try:
|
|
71
|
+
for subscriber in self._subscribers:
|
|
72
|
+
c.subscribe(subscriber)
|
|
73
|
+
|
|
74
|
+
await c.notify_subscribers()
|
|
75
|
+
yield c
|
|
76
|
+
except Exception as e: # noqa: BLE001
|
|
77
|
+
c.task_status.fail(str(e))
|
|
78
|
+
finally:
|
|
79
|
+
c.task_status.complete()
|
|
80
|
+
await c.notify_subscribers()
|
|
81
|
+
|
|
82
|
+
async def skip(self, reason: str) -> None:
|
|
66
83
|
"""Skip the step."""
|
|
67
|
-
self.
|
|
84
|
+
self.task_status.skip(reason)
|
|
68
85
|
|
|
69
86
|
def subscribe(self, subscriber: "ReportingModule") -> None:
|
|
70
87
|
"""Subscribe to the step."""
|
|
71
88
|
self._subscribers.append(subscriber)
|
|
72
89
|
|
|
73
|
-
def set_total(self, total: int) -> None:
|
|
90
|
+
async def set_total(self, total: int) -> None:
|
|
74
91
|
"""Set the total for the step."""
|
|
75
|
-
self.
|
|
76
|
-
self._notify_subscribers()
|
|
92
|
+
self.task_status.set_total(total)
|
|
77
93
|
|
|
78
|
-
def set_current(self, current: int) -> None:
|
|
94
|
+
async def set_current(self, current: int, message: str | None = None) -> None:
|
|
79
95
|
"""Progress the step."""
|
|
80
|
-
self.
|
|
81
|
-
self.
|
|
96
|
+
self.task_status.set_current(current, message)
|
|
97
|
+
await self.notify_subscribers()
|
|
82
98
|
|
|
83
|
-
def
|
|
84
|
-
"""Notify the subscribers."""
|
|
99
|
+
async def notify_subscribers(self) -> None:
|
|
100
|
+
"""Notify the subscribers only if progress has changed."""
|
|
85
101
|
for subscriber in self._subscribers:
|
|
86
|
-
subscriber.on_change(self.
|
|
102
|
+
await subscriber.on_change(self.task_status)
|
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
"""Service for scheduling periodic sync operations."""
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
-
from collections.abc import Callable
|
|
5
4
|
from contextlib import suppress
|
|
6
5
|
|
|
7
6
|
import structlog
|
|
8
|
-
from sqlalchemy.ext.asyncio import AsyncSession
|
|
9
7
|
|
|
10
8
|
from kodit.application.services.queue_service import QueueService
|
|
11
|
-
from kodit.domain.
|
|
12
|
-
from kodit.domain.
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
from kodit.domain.protocols import GitRepoRepository
|
|
10
|
+
from kodit.domain.value_objects import (
|
|
11
|
+
PrescribedOperations,
|
|
12
|
+
QueuePriority,
|
|
13
|
+
)
|
|
16
14
|
|
|
17
15
|
|
|
18
16
|
class SyncSchedulerService:
|
|
@@ -20,10 +18,12 @@ class SyncSchedulerService:
|
|
|
20
18
|
|
|
21
19
|
def __init__(
|
|
22
20
|
self,
|
|
23
|
-
|
|
21
|
+
queue_service: QueueService,
|
|
22
|
+
repo_repository: GitRepoRepository,
|
|
24
23
|
) -> None:
|
|
25
24
|
"""Initialize the sync scheduler service."""
|
|
26
|
-
self.
|
|
25
|
+
self.queue_service = queue_service
|
|
26
|
+
self.repo_repository = repo_repository
|
|
27
27
|
self.log = structlog.get_logger(__name__)
|
|
28
28
|
self._sync_task: asyncio.Task | None = None
|
|
29
29
|
self._shutdown_event = asyncio.Event()
|
|
@@ -67,28 +67,12 @@ class SyncSchedulerService:
|
|
|
67
67
|
"""Perform a sync operation on all indexes."""
|
|
68
68
|
self.log.info("Starting sync operation")
|
|
69
69
|
|
|
70
|
-
#
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
fusion_service=ReciprocalRankFusionService(),
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
# Get all existing indexes
|
|
80
|
-
all_indexes = await index_query_service.list_indexes()
|
|
81
|
-
|
|
82
|
-
if not all_indexes:
|
|
83
|
-
self.log.info("No indexes found to sync")
|
|
84
|
-
return
|
|
85
|
-
|
|
86
|
-
self.log.info("Adding sync tasks to queue", count=len(all_indexes))
|
|
87
|
-
|
|
88
|
-
# Sync each index
|
|
89
|
-
for index in all_indexes:
|
|
90
|
-
await queue_service.enqueue_task(
|
|
91
|
-
Task.create_index_update_task(index.id, QueuePriority.BACKGROUND)
|
|
70
|
+
# Sync each index - queue all 5 tasks with priority ordering
|
|
71
|
+
for repo in await self.repo_repository.get_all():
|
|
72
|
+
await self.queue_service.enqueue_tasks(
|
|
73
|
+
tasks=PrescribedOperations.SYNC_REPOSITORY,
|
|
74
|
+
base_priority=QueuePriority.BACKGROUND,
|
|
75
|
+
payload={"repository_id": repo.id},
|
|
92
76
|
)
|
|
93
77
|
|
|
94
78
|
self.log.info("Sync operation completed")
|