kodit 0.4.1__py3-none-any.whl → 0.4.2__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.

Files changed (42) hide show
  1. kodit/_version.py +2 -2
  2. kodit/app.py +4 -2
  3. kodit/application/factories/code_indexing_factory.py +54 -7
  4. kodit/application/factories/reporting_factory.py +27 -0
  5. kodit/application/services/auto_indexing_service.py +16 -4
  6. kodit/application/services/code_indexing_application_service.py +115 -133
  7. kodit/application/services/indexing_worker_service.py +18 -20
  8. kodit/application/services/queue_service.py +12 -14
  9. kodit/application/services/reporting.py +86 -0
  10. kodit/application/services/sync_scheduler.py +21 -20
  11. kodit/cli.py +14 -18
  12. kodit/config.py +24 -1
  13. kodit/database.py +2 -1
  14. kodit/domain/protocols.py +9 -1
  15. kodit/domain/services/bm25_service.py +1 -6
  16. kodit/domain/services/index_service.py +22 -58
  17. kodit/domain/value_objects.py +57 -9
  18. kodit/infrastructure/api/v1/dependencies.py +23 -10
  19. kodit/infrastructure/cloning/git/working_copy.py +36 -7
  20. kodit/infrastructure/embedding/embedding_factory.py +8 -3
  21. kodit/infrastructure/embedding/embedding_providers/litellm_embedding_provider.py +48 -55
  22. kodit/infrastructure/git/git_utils.py +3 -2
  23. kodit/infrastructure/mappers/index_mapper.py +1 -0
  24. kodit/infrastructure/reporting/__init__.py +1 -0
  25. kodit/infrastructure/reporting/log_progress.py +65 -0
  26. kodit/infrastructure/reporting/tdqm_progress.py +73 -0
  27. kodit/infrastructure/sqlalchemy/embedding_repository.py +47 -68
  28. kodit/infrastructure/sqlalchemy/entities.py +28 -2
  29. kodit/infrastructure/sqlalchemy/index_repository.py +274 -236
  30. kodit/infrastructure/sqlalchemy/task_repository.py +55 -39
  31. kodit/infrastructure/sqlalchemy/unit_of_work.py +59 -0
  32. kodit/mcp.py +10 -2
  33. {kodit-0.4.1.dist-info → kodit-0.4.2.dist-info}/METADATA +1 -1
  34. {kodit-0.4.1.dist-info → kodit-0.4.2.dist-info}/RECORD +37 -36
  35. kodit/domain/interfaces.py +0 -27
  36. kodit/infrastructure/ui/__init__.py +0 -1
  37. kodit/infrastructure/ui/progress.py +0 -170
  38. kodit/infrastructure/ui/spinner.py +0 -74
  39. kodit/reporting.py +0 -78
  40. {kodit-0.4.1.dist-info → kodit-0.4.2.dist-info}/WHEEL +0 -0
  41. {kodit-0.4.1.dist-info → kodit-0.4.2.dist-info}/entry_points.txt +0 -0
  42. {kodit-0.4.1.dist-info → kodit-0.4.2.dist-info}/licenses/LICENSE +0 -0
@@ -2,7 +2,6 @@
2
2
 
3
3
  import asyncio
4
4
  from collections.abc import Callable
5
- from concurrent.futures import ThreadPoolExecutor
6
5
  from contextlib import suppress
7
6
  from datetime import UTC, datetime
8
7
 
@@ -12,10 +11,12 @@ from sqlalchemy.ext.asyncio import AsyncSession
12
11
  from kodit.application.factories.code_indexing_factory import (
13
12
  create_code_indexing_application_service,
14
13
  )
14
+ from kodit.application.factories.reporting_factory import create_noop_operation
15
+ from kodit.application.services.reporting import ProgressTracker
15
16
  from kodit.config import AppContext
16
17
  from kodit.domain.entities import Task
17
18
  from kodit.domain.value_objects import TaskType
18
- from kodit.infrastructure.sqlalchemy.task_repository import SqlAlchemyTaskRepository
19
+ from kodit.infrastructure.sqlalchemy.task_repository import create_task_repository
19
20
 
20
21
 
21
22
  class IndexingWorkerService:
@@ -35,17 +36,16 @@ class IndexingWorkerService:
35
36
  self.session_factory = session_factory
36
37
  self._worker_task: asyncio.Task | None = None
37
38
  self._shutdown_event = asyncio.Event()
38
- self._executor = ThreadPoolExecutor(
39
- max_workers=1, thread_name_prefix="indexing-worker"
40
- )
39
+ self.task_repository = create_task_repository(session_factory)
41
40
  self.log = structlog.get_logger(__name__)
42
41
 
43
- async def start(self) -> None:
42
+ async def start(self, operation: ProgressTracker | None = None) -> None:
44
43
  """Start the worker to process the queue."""
44
+ operation = operation or create_noop_operation()
45
45
  self._running = True
46
46
 
47
47
  # Start single worker task
48
- self._worker_task = asyncio.create_task(self._worker_loop())
48
+ self._worker_task = asyncio.create_task(self._worker_loop(operation))
49
49
 
50
50
  self.log.info(
51
51
  "Indexing worker started",
@@ -57,30 +57,24 @@ class IndexingWorkerService:
57
57
  self._shutdown_event.set()
58
58
 
59
59
  if self._worker_task and not self._worker_task.done():
60
- self._worker_task.cancel()
61
60
  with suppress(asyncio.CancelledError):
61
+ self._worker_task.cancel()
62
62
  await self._worker_task
63
63
 
64
- # Shutdown the thread pool executor
65
- self._executor.shutdown(wait=True)
66
-
67
64
  self.log.info("Indexing worker stopped")
68
65
 
69
- async def _worker_loop(self) -> None:
66
+ async def _worker_loop(self, operation: ProgressTracker) -> None:
70
67
  self.log.debug("Worker loop started")
71
68
 
72
69
  while not self._shutdown_event.is_set():
73
70
  try:
74
71
  async with self.session_factory() as session:
75
- repo = SqlAlchemyTaskRepository(session)
76
- task = await repo.take()
72
+ task = await self.task_repository.take()
77
73
  await session.commit()
78
74
 
79
75
  # If there's a task, process it in a new thread
80
76
  if task:
81
- await asyncio.get_event_loop().run_in_executor(
82
- self._executor, self._process_task, task
83
- )
77
+ await self._process_task(task, operation)
84
78
  continue
85
79
 
86
80
  # If no task, sleep for a bit
@@ -96,7 +90,7 @@ class IndexingWorkerService:
96
90
 
97
91
  self.log.info("Worker loop stopped")
98
92
 
99
- def _process_task(self, task: Task) -> None:
93
+ async def _process_task(self, task: Task, operation: ProgressTracker) -> None:
100
94
  """Process a single task."""
101
95
  self.log.info(
102
96
  "Processing task",
@@ -113,7 +107,7 @@ class IndexingWorkerService:
113
107
  try:
114
108
  # Process based on task type (currently only INDEX_UPDATE is supported)
115
109
  if task.type is TaskType.INDEX_UPDATE:
116
- loop.run_until_complete(self._process_index_update(task))
110
+ await self._process_index_update(task, operation)
117
111
  else:
118
112
  self.log.warning(
119
113
  "Unknown task type",
@@ -131,7 +125,9 @@ class IndexingWorkerService:
131
125
  duration_seconds=duration,
132
126
  )
133
127
 
134
- async def _process_index_update(self, task: Task) -> None:
128
+ async def _process_index_update(
129
+ self, task: Task, operation: ProgressTracker
130
+ ) -> None:
135
131
  """Process index update/sync task."""
136
132
  index_id = task.payload.get("index_id")
137
133
  if not index_id:
@@ -144,6 +140,8 @@ class IndexingWorkerService:
144
140
  service = create_code_indexing_application_service(
145
141
  app_context=self.app_context,
146
142
  session=session,
143
+ session_factory=self.session_factory,
144
+ operation=operation,
147
145
  )
148
146
  index = await service.index_repository.get(index_id)
149
147
  if not index:
@@ -1,11 +1,15 @@
1
1
  """Queue service for managing tasks."""
2
2
 
3
+ from collections.abc import Callable
4
+
3
5
  import structlog
4
6
  from sqlalchemy.ext.asyncio import AsyncSession
5
7
 
6
8
  from kodit.domain.entities import Task
7
9
  from kodit.domain.value_objects import TaskType
8
- from kodit.infrastructure.sqlalchemy.task_repository import SqlAlchemyTaskRepository
10
+ from kodit.infrastructure.sqlalchemy.task_repository import (
11
+ create_task_repository,
12
+ )
9
13
 
10
14
 
11
15
  class QueueService:
@@ -17,26 +21,24 @@ class QueueService:
17
21
 
18
22
  def __init__(
19
23
  self,
20
- session: AsyncSession,
24
+ session_factory: Callable[[], AsyncSession],
21
25
  ) -> None:
22
26
  """Initialize the queue service."""
23
- self.session = session
27
+ self.task_repository = create_task_repository(session_factory=session_factory)
24
28
  self.log = structlog.get_logger(__name__)
25
29
 
26
30
  async def enqueue_task(self, task: Task) -> None:
27
31
  """Queue a task in the database."""
28
- repo = SqlAlchemyTaskRepository(self.session)
29
-
30
32
  # See if task already exists
31
- db_task = await repo.get(task.id)
33
+ db_task = await self.task_repository.get(task.id)
32
34
  if db_task:
33
35
  # Task already exists, update priority
34
36
  db_task.priority = task.priority
35
- await repo.update(db_task)
37
+ await self.task_repository.update(db_task)
36
38
  self.log.info("Task updated", task_id=task.id, task_type=task.type)
37
39
  else:
38
40
  # Otherwise, add task
39
- await repo.add(task)
41
+ await self.task_repository.add(task)
40
42
  self.log.info(
41
43
  "Task queued",
42
44
  task_id=task.id,
@@ -44,14 +46,10 @@ class QueueService:
44
46
  payload=task.payload,
45
47
  )
46
48
 
47
- await self.session.commit()
48
-
49
49
  async def list_tasks(self, task_type: TaskType | None = None) -> list[Task]:
50
50
  """List all tasks in the queue."""
51
- repo = SqlAlchemyTaskRepository(self.session)
52
- return await repo.list(task_type)
51
+ return await self.task_repository.list(task_type)
53
52
 
54
53
  async def get_task(self, task_id: str) -> Task | None:
55
54
  """Get a specific task by ID."""
56
- repo = SqlAlchemyTaskRepository(self.session)
57
- return await repo.get(task_id)
55
+ return await self.task_repository.get(task_id)
@@ -0,0 +1,86 @@
1
+ """Reporting."""
2
+
3
+ from enum import StrEnum
4
+ from types import TracebackType
5
+ from typing import TYPE_CHECKING
6
+
7
+ import structlog
8
+
9
+ from kodit.domain.value_objects import Progress, ReportingState
10
+
11
+ if TYPE_CHECKING:
12
+ from kodit.domain.protocols import ReportingModule
13
+
14
+
15
+ class OperationType(StrEnum):
16
+ """Operation type."""
17
+
18
+ ROOT = "kodit.root"
19
+ CREATE_INDEX = "kodit.index.create"
20
+ RUN_INDEX = "kodit.index.run"
21
+
22
+
23
+ class ProgressTracker:
24
+ """Progress tracker."""
25
+
26
+ def __init__(self, name: str, parent: "ProgressTracker | None" = None) -> None:
27
+ """Initialize the progress tracker."""
28
+ self._parent: ProgressTracker | None = parent
29
+ self._children: list[ProgressTracker] = []
30
+ self._log = structlog.get_logger(__name__)
31
+ 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
+
39
+ def __exit__(
40
+ self,
41
+ exc_type: type[BaseException] | None,
42
+ exc_value: BaseException | None,
43
+ traceback: TracebackType | None,
44
+ ) -> None:
45
+ """Exit the operation."""
46
+ if exc_value:
47
+ self._snapshot = self._snapshot.with_error(exc_value)
48
+ self._snapshot = self._snapshot.with_state(
49
+ ReportingState.FAILED, str(exc_value)
50
+ )
51
+
52
+ if self._snapshot.state == ReportingState.IN_PROGRESS:
53
+ self._snapshot = self._snapshot.with_progress(100)
54
+ self._snapshot = self._snapshot.with_state(ReportingState.COMPLETED)
55
+ self._notify_subscribers()
56
+
57
+ def create_child(self, name: str) -> "ProgressTracker":
58
+ """Create a child step."""
59
+ s = ProgressTracker(name, self)
60
+ self._children.append(s)
61
+ for subscriber in self._subscribers:
62
+ s.subscribe(subscriber)
63
+ return s
64
+
65
+ def skip(self, reason: str | None = None) -> None:
66
+ """Skip the step."""
67
+ self._snapshot = self._snapshot.with_state(ReportingState.SKIPPED, reason or "")
68
+
69
+ def subscribe(self, subscriber: "ReportingModule") -> None:
70
+ """Subscribe to the step."""
71
+ self._subscribers.append(subscriber)
72
+
73
+ def set_total(self, total: int) -> None:
74
+ """Set the total for the step."""
75
+ self._snapshot = self._snapshot.with_total(total)
76
+ self._notify_subscribers()
77
+
78
+ def set_current(self, current: int) -> None:
79
+ """Progress the step."""
80
+ self._snapshot = self._snapshot.with_progress(current)
81
+ self._notify_subscribers()
82
+
83
+ def _notify_subscribers(self) -> None:
84
+ """Notify the subscribers."""
85
+ for subscriber in self._subscribers:
86
+ subscriber.on_change(self._snapshot)
@@ -12,7 +12,7 @@ from kodit.domain.entities import Task
12
12
  from kodit.domain.services.index_query_service import IndexQueryService
13
13
  from kodit.domain.value_objects import QueuePriority
14
14
  from kodit.infrastructure.indexing.fusion_service import ReciprocalRankFusionService
15
- from kodit.infrastructure.sqlalchemy.index_repository import SqlAlchemyIndexRepository
15
+ from kodit.infrastructure.sqlalchemy.index_repository import create_index_repository
16
16
 
17
17
 
18
18
  class SyncSchedulerService:
@@ -67,27 +67,28 @@ class SyncSchedulerService:
67
67
  """Perform a sync operation on all indexes."""
68
68
  self.log.info("Starting sync operation")
69
69
 
70
- async with self.session_factory() as session:
71
- # Create services
72
- queue_service = QueueService(session=session)
73
- index_query_service = IndexQueryService(
74
- index_repository=SqlAlchemyIndexRepository(session=session),
75
- fusion_service=ReciprocalRankFusionService(),
76
- )
70
+ # Create services
71
+ queue_service = QueueService(session_factory=self.session_factory)
72
+ index_query_service = IndexQueryService(
73
+ index_repository=create_index_repository(
74
+ session_factory=self.session_factory
75
+ ),
76
+ fusion_service=ReciprocalRankFusionService(),
77
+ )
77
78
 
78
- # Get all existing indexes
79
- all_indexes = await index_query_service.list_indexes()
79
+ # Get all existing indexes
80
+ all_indexes = await index_query_service.list_indexes()
80
81
 
81
- if not all_indexes:
82
- self.log.info("No indexes found to sync")
83
- return
82
+ if not all_indexes:
83
+ self.log.info("No indexes found to sync")
84
+ return
84
85
 
85
- self.log.info("Adding sync tasks to queue", count=len(all_indexes))
86
+ self.log.info("Adding sync tasks to queue", count=len(all_indexes))
86
87
 
87
- # Sync each index
88
- for index in all_indexes:
89
- await queue_service.enqueue_task(
90
- Task.create_index_update_task(index.id, QueuePriority.BACKGROUND)
91
- )
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)
92
+ )
92
93
 
93
- self.log.info("Sync operation completed")
94
+ self.log.info("Sync operation completed")
kodit/cli.py CHANGED
@@ -11,7 +11,7 @@ import uvicorn
11
11
  from pytable_formatter import Cell, Table # type: ignore[import-untyped]
12
12
 
13
13
  from kodit.application.factories.code_indexing_factory import (
14
- create_code_indexing_application_service,
14
+ create_cli_code_indexing_application_service,
15
15
  )
16
16
  from kodit.config import (
17
17
  AppContext,
@@ -27,11 +27,7 @@ from kodit.domain.value_objects import (
27
27
  )
28
28
  from kodit.infrastructure.api.client import IndexClient, SearchClient
29
29
  from kodit.infrastructure.indexing.fusion_service import ReciprocalRankFusionService
30
- from kodit.infrastructure.sqlalchemy.index_repository import SqlAlchemyIndexRepository
31
- from kodit.infrastructure.ui.progress import (
32
- create_lazy_progress_callback,
33
- create_multi_stage_progress_callback,
34
- )
30
+ from kodit.infrastructure.sqlalchemy.index_repository import create_index_repository
35
31
  from kodit.log import configure_logging, configure_telemetry, log_event
36
32
  from kodit.mcp import create_stdio_mcp_server
37
33
 
@@ -119,11 +115,8 @@ async def _handle_sync(
119
115
  for index in indexes_to_sync:
120
116
  click.echo(f"Syncing: {index.source.working_copy.remote_uri}")
121
117
 
122
- # Create progress callback for this sync operation
123
- progress_callback = create_multi_stage_progress_callback()
124
-
125
118
  try:
126
- await service.run_index(index, progress_callback)
119
+ await service.run_index(index)
127
120
  click.echo(f"✓ Sync completed: {index.source.working_copy.remote_uri}")
128
121
  except Exception as e:
129
122
  log.exception("Sync failed", index_id=index.id, error=e)
@@ -191,12 +184,15 @@ async def _index_local(
191
184
  # Get database session
192
185
  db = await app_context.get_db()
193
186
  async with db.session_factory() as session:
194
- service = create_code_indexing_application_service(
187
+ service = create_cli_code_indexing_application_service(
195
188
  app_context=app_context,
196
189
  session=session,
190
+ session_factory=db.session_factory,
197
191
  )
198
192
  index_query_service = IndexQueryService(
199
- index_repository=SqlAlchemyIndexRepository(session=session),
193
+ index_repository=create_index_repository(
194
+ session_factory=db.session_factory
195
+ ),
200
196
  fusion_service=ReciprocalRankFusionService(),
201
197
  )
202
198
 
@@ -223,13 +219,11 @@ async def _index_local(
223
219
  log_event("kodit.cli.index.create")
224
220
 
225
221
  # Create a lazy progress callback that only shows progress when needed
226
- progress_callback = create_lazy_progress_callback()
227
- index = await service.create_index_from_uri(source, progress_callback)
222
+ index = await service.create_index_from_uri(source)
228
223
 
229
224
  # Create a new progress callback for the indexing operations
230
- indexing_progress_callback = create_multi_stage_progress_callback()
231
225
  try:
232
- await service.run_index(index, indexing_progress_callback)
226
+ await service.run_index(index)
233
227
  except EmptySourceError as e:
234
228
  log.exception("Empty source error", error=e)
235
229
  msg = f"""{e}. This could mean:
@@ -326,9 +320,10 @@ async def _search_local( # noqa: PLR0913
326
320
  # Get database session
327
321
  db = await app_context.get_db()
328
322
  async with db.session_factory() as session:
329
- service = create_code_indexing_application_service(
323
+ service = create_cli_code_indexing_application_service(
330
324
  app_context=app_context,
331
325
  session=session,
326
+ session_factory=db.session_factory,
332
327
  )
333
328
 
334
329
  filters = _parse_filters(
@@ -791,9 +786,10 @@ async def snippets(
791
786
  log_event("kodit.cli.show.snippets")
792
787
  db = await app_context.get_db()
793
788
  async with db.session_factory() as session:
794
- service = create_code_indexing_application_service(
789
+ service = create_cli_code_indexing_application_service(
795
790
  app_context=app_context,
796
791
  session=session,
792
+ session_factory=db.session_factory,
797
793
  )
798
794
  snippets = await service.list_snippets(
799
795
  file_path=by_path, source_uri=by_source
kodit/config.py CHANGED
@@ -3,6 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import asyncio
6
+ from datetime import timedelta
6
7
  from enum import Enum
7
8
  from functools import wraps
8
9
  from pathlib import Path
@@ -41,6 +42,15 @@ T = TypeVar("T")
41
42
  EndpointType = Literal["openai", "litellm"]
42
43
 
43
44
 
45
+ class ReportingConfig(BaseModel):
46
+ """Reporting configuration."""
47
+
48
+ log_time_interval: timedelta = Field(
49
+ default=timedelta(seconds=5),
50
+ description="Time interval to log progress in seconds",
51
+ )
52
+
53
+
44
54
  class Endpoint(BaseModel):
45
55
  """Endpoint provides configuration for an AI service."""
46
56
 
@@ -50,7 +60,10 @@ class Endpoint(BaseModel):
50
60
  description="Model to use for the endpoint in litellm format (e.g. 'openai/text-embedding-3-small')", # noqa: E501
51
61
  )
52
62
  api_key: str | None = None
53
- num_parallel_tasks: int | None = None
63
+ num_parallel_tasks: int = Field(
64
+ default=10,
65
+ description="Number of parallel tasks to use for the endpoint",
66
+ )
54
67
  socket_path: str | None = Field(
55
68
  default=None,
56
69
  description="Unix socket path for local communication (e.g., /tmp/openai.sock)",
@@ -63,6 +76,13 @@ class Endpoint(BaseModel):
63
76
  default=None,
64
77
  description="Extra provider-specific non-secret parameters for LiteLLM",
65
78
  )
79
+ max_tokens: int = Field(
80
+ default=8000, # Reasonable default (with headroom) for most models.
81
+ description="Conservative token limit for the embedding model",
82
+ )
83
+
84
+
85
+ DEFAULT_NUM_PARALLEL_TASKS = 10 # Semaphore limit for concurrent requests
66
86
 
67
87
 
68
88
  class Search(BaseModel):
@@ -224,6 +244,9 @@ class AppContext(BaseSettings):
224
244
  remote: RemoteConfig = Field(
225
245
  default_factory=RemoteConfig, description="Remote server configuration"
226
246
  )
247
+ reporting: ReportingConfig = Field(
248
+ default=ReportingConfig(), description="Reporting configuration"
249
+ )
227
250
 
228
251
  @field_validator("api_keys", mode="before")
229
252
  @classmethod
kodit/database.py CHANGED
@@ -1,5 +1,6 @@
1
1
  """Database configuration for kodit."""
2
2
 
3
+ from collections.abc import Callable
3
4
  from pathlib import Path
4
5
 
5
6
  import structlog
@@ -28,7 +29,7 @@ class Database:
28
29
  )
29
30
 
30
31
  @property
31
- def session_factory(self) -> async_sessionmaker[AsyncSession]:
32
+ def session_factory(self) -> Callable[[], AsyncSession]:
32
33
  """Get the session factory."""
33
34
  return self.db_session_factory
34
35
 
kodit/domain/protocols.py CHANGED
@@ -6,7 +6,7 @@ from typing import Protocol
6
6
  from pydantic import AnyUrl
7
7
 
8
8
  from kodit.domain.entities import Index, Snippet, SnippetWithContext, Task, WorkingCopy
9
- from kodit.domain.value_objects import MultiSearchRequest, TaskType
9
+ from kodit.domain.value_objects import MultiSearchRequest, Progress, TaskType
10
10
 
11
11
 
12
12
  class TaskRepository(Protocol):
@@ -90,3 +90,11 @@ class IndexRepository(Protocol):
90
90
  async def get_snippets_by_ids(self, ids: list[int]) -> list[SnippetWithContext]:
91
91
  """Get snippets by their IDs."""
92
92
  ...
93
+
94
+
95
+ class ReportingModule(Protocol):
96
+ """Reporting module."""
97
+
98
+ def on_change(self, step: Progress) -> None:
99
+ """On step changed."""
100
+ ...
@@ -31,12 +31,7 @@ class BM25DomainService:
31
31
  """Domain service for BM25 operations."""
32
32
 
33
33
  def __init__(self, repository: BM25Repository) -> None:
34
- """Initialize the BM25 domain service.
35
-
36
- Args:
37
- repository: The BM25 repository for persistence operations
38
-
39
- """
34
+ """Initialize the BM25 domain service."""
40
35
  self.repository = repository
41
36
 
42
37
  async def index_documents(self, request: IndexRequest) -> None: