kodit 0.4.3__py3-none-any.whl → 0.5.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of kodit might be problematic. Click here for more details.

Files changed (135) hide show
  1. kodit/_version.py +2 -2
  2. kodit/app.py +51 -23
  3. kodit/application/factories/reporting_factory.py +6 -2
  4. kodit/application/factories/server_factory.py +353 -0
  5. kodit/application/services/code_search_application_service.py +144 -0
  6. kodit/application/services/commit_indexing_application_service.py +700 -0
  7. kodit/application/services/indexing_worker_service.py +13 -44
  8. kodit/application/services/queue_service.py +24 -3
  9. kodit/application/services/reporting.py +0 -2
  10. kodit/application/services/sync_scheduler.py +15 -31
  11. kodit/cli.py +2 -753
  12. kodit/cli_utils.py +2 -9
  13. kodit/config.py +4 -97
  14. kodit/database.py +38 -1
  15. kodit/domain/enrichments/__init__.py +1 -0
  16. kodit/domain/enrichments/architecture/__init__.py +1 -0
  17. kodit/domain/enrichments/architecture/architecture.py +20 -0
  18. kodit/domain/enrichments/architecture/physical/__init__.py +1 -0
  19. kodit/domain/enrichments/architecture/physical/discovery_notes.py +14 -0
  20. kodit/domain/enrichments/architecture/physical/formatter.py +11 -0
  21. kodit/domain/enrichments/architecture/physical/physical.py +17 -0
  22. kodit/domain/enrichments/development/__init__.py +1 -0
  23. kodit/domain/enrichments/development/development.py +18 -0
  24. kodit/domain/enrichments/development/snippet/__init__.py +1 -0
  25. kodit/domain/enrichments/development/snippet/snippet.py +21 -0
  26. kodit/domain/enrichments/enricher.py +17 -0
  27. kodit/domain/enrichments/enrichment.py +39 -0
  28. kodit/domain/enrichments/request.py +12 -0
  29. kodit/domain/enrichments/response.py +11 -0
  30. kodit/domain/enrichments/usage/__init__.py +1 -0
  31. kodit/domain/enrichments/usage/api_docs.py +19 -0
  32. kodit/domain/enrichments/usage/usage.py +18 -0
  33. kodit/domain/{entities.py → entities/__init__.py} +50 -195
  34. kodit/domain/entities/git.py +190 -0
  35. kodit/domain/factories/__init__.py +1 -0
  36. kodit/domain/factories/git_repo_factory.py +76 -0
  37. kodit/domain/protocols.py +264 -64
  38. kodit/domain/services/bm25_service.py +5 -1
  39. kodit/domain/services/embedding_service.py +3 -0
  40. kodit/domain/services/enrichment_service.py +9 -30
  41. kodit/domain/services/git_repository_service.py +429 -0
  42. kodit/domain/services/git_service.py +300 -0
  43. kodit/domain/services/physical_architecture_service.py +182 -0
  44. kodit/domain/services/task_status_query_service.py +2 -2
  45. kodit/domain/value_objects.py +87 -135
  46. kodit/infrastructure/api/client/__init__.py +0 -2
  47. kodit/infrastructure/api/v1/__init__.py +0 -4
  48. kodit/infrastructure/api/v1/dependencies.py +92 -46
  49. kodit/infrastructure/api/v1/routers/__init__.py +0 -6
  50. kodit/infrastructure/api/v1/routers/commits.py +352 -0
  51. kodit/infrastructure/api/v1/routers/queue.py +2 -2
  52. kodit/infrastructure/api/v1/routers/repositories.py +282 -0
  53. kodit/infrastructure/api/v1/routers/search.py +31 -14
  54. kodit/infrastructure/api/v1/schemas/__init__.py +0 -24
  55. kodit/infrastructure/api/v1/schemas/commit.py +96 -0
  56. kodit/infrastructure/api/v1/schemas/context.py +2 -0
  57. kodit/infrastructure/api/v1/schemas/enrichment.py +29 -0
  58. kodit/infrastructure/api/v1/schemas/repository.py +128 -0
  59. kodit/infrastructure/api/v1/schemas/search.py +12 -9
  60. kodit/infrastructure/api/v1/schemas/snippet.py +58 -0
  61. kodit/infrastructure/api/v1/schemas/tag.py +31 -0
  62. kodit/infrastructure/api/v1/schemas/task_status.py +2 -0
  63. kodit/infrastructure/bm25/local_bm25_repository.py +16 -4
  64. kodit/infrastructure/bm25/vectorchord_bm25_repository.py +68 -52
  65. kodit/infrastructure/cloning/git/git_python_adaptor.py +534 -0
  66. kodit/infrastructure/cloning/git/working_copy.py +1 -1
  67. kodit/infrastructure/embedding/embedding_factory.py +3 -2
  68. kodit/infrastructure/embedding/local_vector_search_repository.py +1 -1
  69. kodit/infrastructure/embedding/vectorchord_vector_search_repository.py +111 -84
  70. kodit/infrastructure/enricher/__init__.py +1 -0
  71. kodit/infrastructure/enricher/enricher_factory.py +53 -0
  72. kodit/infrastructure/{enrichment/litellm_enrichment_provider.py → enricher/litellm_enricher.py} +36 -56
  73. kodit/infrastructure/{enrichment/local_enrichment_provider.py → enricher/local_enricher.py} +19 -24
  74. kodit/infrastructure/enricher/null_enricher.py +36 -0
  75. kodit/infrastructure/indexing/fusion_service.py +1 -1
  76. kodit/infrastructure/mappers/enrichment_mapper.py +83 -0
  77. kodit/infrastructure/mappers/git_mapper.py +193 -0
  78. kodit/infrastructure/mappers/snippet_mapper.py +104 -0
  79. kodit/infrastructure/mappers/task_mapper.py +5 -44
  80. kodit/infrastructure/physical_architecture/__init__.py +1 -0
  81. kodit/infrastructure/physical_architecture/detectors/__init__.py +1 -0
  82. kodit/infrastructure/physical_architecture/detectors/docker_compose_detector.py +336 -0
  83. kodit/infrastructure/physical_architecture/formatters/__init__.py +1 -0
  84. kodit/infrastructure/physical_architecture/formatters/narrative_formatter.py +149 -0
  85. kodit/infrastructure/reporting/log_progress.py +8 -5
  86. kodit/infrastructure/reporting/telemetry_progress.py +21 -0
  87. kodit/infrastructure/slicing/api_doc_extractor.py +836 -0
  88. kodit/infrastructure/slicing/ast_analyzer.py +1128 -0
  89. kodit/infrastructure/slicing/slicer.py +87 -421
  90. kodit/infrastructure/sqlalchemy/embedding_repository.py +43 -23
  91. kodit/infrastructure/sqlalchemy/enrichment_v2_repository.py +118 -0
  92. kodit/infrastructure/sqlalchemy/entities.py +402 -158
  93. kodit/infrastructure/sqlalchemy/git_branch_repository.py +274 -0
  94. kodit/infrastructure/sqlalchemy/git_commit_repository.py +346 -0
  95. kodit/infrastructure/sqlalchemy/git_repository.py +262 -0
  96. kodit/infrastructure/sqlalchemy/git_tag_repository.py +268 -0
  97. kodit/infrastructure/sqlalchemy/snippet_v2_repository.py +479 -0
  98. kodit/infrastructure/sqlalchemy/task_repository.py +29 -23
  99. kodit/infrastructure/sqlalchemy/task_status_repository.py +24 -12
  100. kodit/infrastructure/sqlalchemy/unit_of_work.py +10 -14
  101. kodit/mcp.py +12 -30
  102. kodit/migrations/env.py +1 -0
  103. kodit/migrations/versions/04b80f802e0c_foreign_key_review.py +100 -0
  104. kodit/migrations/versions/19f8c7faf8b9_add_generic_enrichment_type.py +260 -0
  105. kodit/migrations/versions/7f15f878c3a1_add_new_git_entities.py +690 -0
  106. kodit/migrations/versions/f9e5ef5e688f_add_git_commits_number.py +43 -0
  107. kodit/py.typed +0 -0
  108. kodit/utils/dump_config.py +361 -0
  109. kodit/utils/dump_openapi.py +6 -4
  110. kodit/utils/path_utils.py +29 -0
  111. {kodit-0.4.3.dist-info → kodit-0.5.1.dist-info}/METADATA +3 -3
  112. kodit-0.5.1.dist-info/RECORD +168 -0
  113. kodit/application/factories/code_indexing_factory.py +0 -195
  114. kodit/application/services/auto_indexing_service.py +0 -99
  115. kodit/application/services/code_indexing_application_service.py +0 -410
  116. kodit/domain/services/index_query_service.py +0 -70
  117. kodit/domain/services/index_service.py +0 -269
  118. kodit/infrastructure/api/client/index_client.py +0 -57
  119. kodit/infrastructure/api/v1/routers/indexes.py +0 -164
  120. kodit/infrastructure/api/v1/schemas/index.py +0 -101
  121. kodit/infrastructure/bm25/bm25_factory.py +0 -28
  122. kodit/infrastructure/cloning/__init__.py +0 -1
  123. kodit/infrastructure/cloning/metadata.py +0 -98
  124. kodit/infrastructure/enrichment/__init__.py +0 -1
  125. kodit/infrastructure/enrichment/enrichment_factory.py +0 -52
  126. kodit/infrastructure/enrichment/null_enrichment_provider.py +0 -19
  127. kodit/infrastructure/mappers/index_mapper.py +0 -345
  128. kodit/infrastructure/reporting/tdqm_progress.py +0 -38
  129. kodit/infrastructure/slicing/language_detection_service.py +0 -18
  130. kodit/infrastructure/sqlalchemy/index_repository.py +0 -646
  131. kodit-0.4.3.dist-info/RECORD +0 -125
  132. /kodit/infrastructure/{enrichment → enricher}/utils.py +0 -0
  133. {kodit-0.4.3.dist-info → kodit-0.5.1.dist-info}/WHEEL +0 -0
  134. {kodit-0.4.3.dist-info → kodit-0.5.1.dist-info}/entry_points.txt +0 -0
  135. {kodit-0.4.3.dist-info → kodit-0.5.1.dist-info}/licenses/LICENSE +0 -0
kodit/cli_utils.py CHANGED
@@ -6,7 +6,7 @@ from typing import TYPE_CHECKING, Any
6
6
 
7
7
  import click
8
8
 
9
- from kodit.infrastructure.api.client import IndexClient, SearchClient
9
+ from kodit.infrastructure.api.client import SearchClient
10
10
 
11
11
  if TYPE_CHECKING:
12
12
  from kodit.config import AppContext
@@ -37,7 +37,7 @@ def with_client(f: Callable) -> Callable:
37
37
  inner_func = getattr(
38
38
  getattr(session_wrapped, "__wrapped__", session_wrapped),
39
39
  "__wrapped__",
40
- session_wrapped
40
+ session_wrapped,
41
41
  )
42
42
 
43
43
  # Get database session manually
@@ -47,13 +47,6 @@ def with_client(f: Callable) -> Callable:
47
47
  else:
48
48
  # Remote mode - use API clients
49
49
  clients = {
50
- "index_client": IndexClient(
51
- base_url=app_context.remote.server_url or "",
52
- api_key=app_context.remote.api_key,
53
- timeout=app_context.remote.timeout,
54
- max_retries=app_context.remote.max_retries,
55
- verify_ssl=app_context.remote.verify_ssl,
56
- ),
57
50
  "search_client": SearchClient(
58
51
  base_url=app_context.remote.server_url or "",
59
52
  api_key=app_context.remote.api_key,
kodit/config.py CHANGED
@@ -14,9 +14,7 @@ import structlog
14
14
  from pydantic import BaseModel, Field, field_validator
15
15
  from pydantic_settings import (
16
16
  BaseSettings,
17
- EnvSettingsSource,
18
17
  NoDecode,
19
- PydanticBaseSettingsSource,
20
18
  SettingsConfigDict,
21
19
  )
22
20
 
@@ -68,9 +66,9 @@ class Endpoint(BaseModel):
68
66
  default=None,
69
67
  description="Unix socket path for local communication (e.g., /tmp/openai.sock)",
70
68
  )
71
- timeout: float | None = Field(
72
- default=None,
73
- description="Request timeout in seconds (default: 30.0)",
69
+ timeout: float = Field(
70
+ default=60,
71
+ description="Request timeout in seconds",
74
72
  )
75
73
  extra_params: dict[str, Any] | None = Field(
76
74
  default=None,
@@ -91,46 +89,12 @@ class Search(BaseModel):
91
89
  provider: Literal["sqlite", "vectorchord"] = Field(default="sqlite")
92
90
 
93
91
 
94
- class AutoIndexingSource(BaseModel):
95
- """Configuration for a single auto-indexing source."""
96
-
97
- uri: str = Field(description="URI of the source to index (git URL or local path)")
98
-
99
-
100
- class AutoIndexingConfig(BaseModel):
101
- """Configuration for auto-indexing."""
102
-
103
- sources: list[AutoIndexingSource] = Field(
104
- default_factory=list, description="List of sources to auto-index"
105
- )
106
-
107
- @field_validator("sources", mode="before")
108
- @classmethod
109
- def parse_sources(cls, v: Any) -> Any:
110
- """Parse sources from environment variables or other formats."""
111
- if v is None:
112
- return []
113
- if isinstance(v, list):
114
- return v
115
- if isinstance(v, dict):
116
- # Handle case where env vars are numbered keys like {'0': {'uri': '...'}}
117
- sources = []
118
- i = 0
119
- while str(i) in v:
120
- source_data = v[str(i)]
121
- if isinstance(source_data, dict) and "uri" in source_data:
122
- sources.append(AutoIndexingSource(uri=source_data["uri"]))
123
- i += 1
124
- return sources
125
- return v
126
-
127
-
128
92
  class PeriodicSyncConfig(BaseModel):
129
93
  """Configuration for periodic/scheduled syncing."""
130
94
 
131
95
  enabled: bool = Field(default=True, description="Enable periodic sync")
132
96
  interval_seconds: float = Field(
133
- default=1800, description="Interval between automatic syncs in seconds"
97
+ default=1800, description="Interval between periodic syncs in seconds"
134
98
  )
135
99
  retry_attempts: int = Field(
136
100
  default=3, description="Number of retry attempts for failed syncs"
@@ -147,36 +111,6 @@ class RemoteConfig(BaseModel):
147
111
  verify_ssl: bool = Field(default=True, description="Verify SSL certificates")
148
112
 
149
113
 
150
- class CustomAutoIndexingEnvSource(EnvSettingsSource):
151
- """Custom environment source for parsing AutoIndexingConfig."""
152
-
153
- def __call__(self) -> dict[str, Any]:
154
- """Load settings from env vars with custom auto-indexing parsing."""
155
- d: dict[str, Any] = {}
156
-
157
- # First get the standard env vars
158
- env_vars = super().__call__()
159
- d.update(env_vars)
160
-
161
- # Custom parsing for auto-indexing sources
162
- auto_indexing_sources = []
163
- i = 0
164
- while True:
165
- # Note: env_vars keys are lowercase due to Pydantic Settings normalization
166
- uri_key = f"auto_indexing_sources_{i}_uri"
167
- if uri_key in self.env_vars:
168
- uri_value = self.env_vars[uri_key]
169
- auto_indexing_sources.append({"uri": uri_value})
170
- i += 1
171
- else:
172
- break
173
-
174
- if auto_indexing_sources:
175
- d["auto_indexing"] = {"sources": auto_indexing_sources}
176
-
177
- return d
178
-
179
-
180
114
  class AppContext(BaseSettings):
181
115
  """Global context for the kodit project. Provides a shared state for the app."""
182
116
 
@@ -189,30 +123,6 @@ class AppContext(BaseSettings):
189
123
  extra="ignore",
190
124
  )
191
125
 
192
- @classmethod
193
- def settings_customise_sources(
194
- cls,
195
- settings_cls: type[BaseSettings],
196
- init_settings: PydanticBaseSettingsSource,
197
- env_settings: PydanticBaseSettingsSource, # noqa: ARG003
198
- dotenv_settings: PydanticBaseSettingsSource,
199
- file_secret_settings: PydanticBaseSettingsSource,
200
- ) -> tuple[PydanticBaseSettingsSource, ...]:
201
- """Customize settings sources to use custom auto-indexing parsing."""
202
- custom_env_settings = CustomAutoIndexingEnvSource(
203
- settings_cls,
204
- env_nested_delimiter=settings_cls.model_config.get("env_nested_delimiter"),
205
- env_ignore_empty=settings_cls.model_config.get("env_ignore_empty", False),
206
- env_parse_none_str=settings_cls.model_config.get("env_parse_none_str", ""),
207
- env_parse_enums=settings_cls.model_config.get("env_parse_enums", None),
208
- )
209
- return (
210
- init_settings,
211
- custom_env_settings,
212
- dotenv_settings,
213
- file_secret_settings,
214
- )
215
-
216
126
  data_dir: Path = Field(default=DEFAULT_BASE_DIR)
217
127
  db_url: str = Field(
218
128
  default_factory=lambda data: f"sqlite+aiosqlite:///{data['data_dir']}/kodit.db"
@@ -231,9 +141,6 @@ class AppContext(BaseSettings):
231
141
  default_search: Search = Field(
232
142
  default=Search(),
233
143
  )
234
- auto_indexing: AutoIndexingConfig | None = Field(
235
- default=AutoIndexingConfig(), description="Auto-indexing configuration"
236
- )
237
144
  periodic_sync: PeriodicSyncConfig = Field(
238
145
  default=PeriodicSyncConfig(), description="Periodic sync configuration"
239
146
  )
kodit/database.py CHANGED
@@ -2,10 +2,12 @@
2
2
 
3
3
  from collections.abc import Callable
4
4
  from pathlib import Path
5
+ from typing import Any
5
6
 
6
7
  import structlog
7
8
  from alembic import command
8
9
  from alembic.config import Config as AlembicConfig
10
+ from sqlalchemy import event
9
11
  from sqlalchemy.ext.asyncio import (
10
12
  AsyncSession,
11
13
  async_sessionmaker,
@@ -21,7 +23,42 @@ class Database:
21
23
  def __init__(self, db_url: str) -> None:
22
24
  """Initialize the database."""
23
25
  self.log = structlog.get_logger(__name__)
24
- self.db_engine = create_async_engine(db_url, echo=False)
26
+
27
+ # Configure SQLite-specific connection arguments to prevent locking issues
28
+ connect_args = {}
29
+ if "sqlite" in db_url.lower():
30
+ connect_args = {
31
+ "timeout": 20, # 20 second timeout for database operations
32
+ "check_same_thread": False, # Allow use from different threads
33
+ }
34
+
35
+ self.db_engine = create_async_engine(
36
+ db_url,
37
+ echo=False,
38
+ connect_args=connect_args,
39
+ )
40
+
41
+ # Configure SQLite pragmas for better concurrency and performance
42
+ if "sqlite" in db_url.lower():
43
+
44
+ @event.listens_for(self.db_engine.sync_engine, "connect")
45
+ def set_sqlite_pragma(
46
+ dbapi_connection: Any, connection_record: Any
47
+ ) -> None:
48
+ del (
49
+ connection_record
50
+ ) # Unused but required by SQLAlchemy event interface
51
+ cursor = dbapi_connection.cursor()
52
+ # Enable WAL mode for better concurrency
53
+ cursor.execute("PRAGMA journal_mode=WAL")
54
+ # Set busy timeout to prevent immediate locking failures
55
+ cursor.execute("PRAGMA busy_timeout=20000")
56
+ # Enable foreign key constraints
57
+ cursor.execute("PRAGMA foreign_keys=ON")
58
+ # Optimize for speed over safety (acceptable for indexing workloads)
59
+ cursor.execute("PRAGMA synchronous=NORMAL")
60
+ cursor.close()
61
+
25
62
  self.db_session_factory = async_sessionmaker(
26
63
  self.db_engine,
27
64
  class_=AsyncSession,
@@ -0,0 +1 @@
1
+ """Enrichment domain package."""
@@ -0,0 +1 @@
1
+ """Architecture enrichment package."""
@@ -0,0 +1,20 @@
1
+ """Architecture enrichment domain entity."""
2
+
3
+ from abc import ABC
4
+ from dataclasses import dataclass
5
+
6
+ from kodit.domain.enrichments.enrichment import (
7
+ CommitEnrichment,
8
+ )
9
+
10
+ ENRICHMENT_TYPE_ARCHITECTURE = "architecture"
11
+
12
+
13
+ @dataclass
14
+ class ArchitectureEnrichment(CommitEnrichment, ABC):
15
+ """Enrichment containing physical architecture discovery for a commit."""
16
+
17
+ @property
18
+ def type(self) -> str:
19
+ """Return the enrichment type."""
20
+ return ENRICHMENT_TYPE_ARCHITECTURE
@@ -0,0 +1 @@
1
+ """Physical architecture enrichment package."""
@@ -0,0 +1,14 @@
1
+ """Physical architecture domain value objects."""
2
+
3
+ from dataclasses import dataclass
4
+
5
+
6
+ @dataclass
7
+ class ArchitectureDiscoveryNotes:
8
+ """Rich, narrative observations about repository architecture for LLM consumption.""" # noqa: E501
9
+
10
+ repository_context: str # High-level overview and discovery scope
11
+ component_observations: list[str] # Detailed findings about each component
12
+ connection_observations: list[str] # How components interact and communicate
13
+ infrastructure_observations: list[str] # Deployment, config, operational patterns
14
+ discovery_metadata: str # Methodology, confidence, limitations, timestamp
@@ -0,0 +1,11 @@
1
+ """Physical architecture formatter protocol."""
2
+
3
+ from typing import Any, Protocol
4
+
5
+
6
+ class PhysicalArchitectureFormatter(Protocol):
7
+ """Formatter for converting architecture discovery notes to LLM-optimized text."""
8
+
9
+ def format_for_llm(self, notes: Any) -> str:
10
+ """Format architecture discovery notes for LLM consumption."""
11
+ ...
@@ -0,0 +1,17 @@
1
+ """Physical architecture enrichment domain entity."""
2
+
3
+ from dataclasses import dataclass
4
+
5
+ from kodit.domain.enrichments.architecture.architecture import ArchitectureEnrichment
6
+
7
+ ENRICHMENT_SUBTYPE_PHYSICAL = "physical"
8
+
9
+
10
+ @dataclass
11
+ class PhysicalArchitectureEnrichment(ArchitectureEnrichment):
12
+ """Enrichment containing physical architecture discovery for a commit."""
13
+
14
+ @property
15
+ def subtype(self) -> str | None:
16
+ """Return the enrichment subtype."""
17
+ return ENRICHMENT_SUBTYPE_PHYSICAL
@@ -0,0 +1 @@
1
+ """Development enrichment package."""
@@ -0,0 +1,18 @@
1
+ """Development enrichment domain entity."""
2
+
3
+ from abc import ABC
4
+ from dataclasses import dataclass
5
+
6
+ from kodit.domain.enrichments.enrichment import CommitEnrichment
7
+
8
+ ENRICHMENT_TYPE_DEVELOPMENT = "development"
9
+
10
+
11
+ @dataclass
12
+ class DevelopmentEnrichment(CommitEnrichment, ABC):
13
+ """Enrichment containing development discovery for a commit."""
14
+
15
+ @property
16
+ def type(self) -> str:
17
+ """Return the enrichment type."""
18
+ return ENRICHMENT_TYPE_DEVELOPMENT
@@ -0,0 +1 @@
1
+ """Snippet enrichment package."""
@@ -0,0 +1,21 @@
1
+ """Snippet enrichment domain entity."""
2
+
3
+ from dataclasses import dataclass
4
+
5
+ from kodit.domain.enrichments.development.development import DevelopmentEnrichment
6
+
7
+ ENRICHMENT_SUBTYPE_SNIPPET_SUMMARY = "snippet_summary"
8
+
9
+
10
+ @dataclass
11
+ class SnippetEnrichment(DevelopmentEnrichment):
12
+ """Enrichment specific to code snippets."""
13
+
14
+ @property
15
+ def subtype(self) -> str | None:
16
+ """Return the enrichment subtype."""
17
+ return ENRICHMENT_SUBTYPE_SNIPPET_SUMMARY
18
+
19
+ def entity_type_key(self) -> str:
20
+ """Return the entity type key this enrichment is for."""
21
+ return "snippet_v2"
@@ -0,0 +1,17 @@
1
+ """Enricher interface."""
2
+
3
+ from collections.abc import AsyncGenerator
4
+ from typing import Protocol
5
+
6
+ from kodit.domain.enrichments.request import EnrichmentRequest
7
+ from kodit.domain.enrichments.response import EnrichmentResponse
8
+
9
+
10
+ class Enricher(Protocol):
11
+ """Interface for text enrichment with custom prompts."""
12
+
13
+ def enrich(
14
+ self, requests: list[EnrichmentRequest]
15
+ ) -> AsyncGenerator[EnrichmentResponse, None]:
16
+ """Enrich a list of requests with custom system prompts."""
17
+ ...
@@ -0,0 +1,39 @@
1
+ """Enrichment domain entities."""
2
+
3
+ from abc import ABC, abstractmethod
4
+ from dataclasses import dataclass
5
+ from datetime import datetime
6
+
7
+
8
+ @dataclass
9
+ class EnrichmentV2(ABC):
10
+ """Generic enrichment that can be attached to any entity."""
11
+
12
+ entity_id: str
13
+ content: str = ""
14
+ id: int | None = None
15
+ created_at: datetime | None = None
16
+ updated_at: datetime | None = None
17
+
18
+ @property
19
+ @abstractmethod
20
+ def type(self) -> str:
21
+ """Return the enrichment type."""
22
+
23
+ @property
24
+ @abstractmethod
25
+ def subtype(self) -> str | None:
26
+ """Return the enrichment subtype (optional for hierarchical types)."""
27
+
28
+ @abstractmethod
29
+ def entity_type_key(self) -> str:
30
+ """Return the entity type key this enrichment is for."""
31
+
32
+
33
+ @dataclass
34
+ class CommitEnrichment(EnrichmentV2, ABC):
35
+ """Enrichment specific to commits."""
36
+
37
+ def entity_type_key(self) -> str:
38
+ """Return the entity type key this enrichment is for."""
39
+ return "git_commit"
@@ -0,0 +1,12 @@
1
+ """Generic enrichment request value object."""
2
+
3
+ from dataclasses import dataclass
4
+
5
+
6
+ @dataclass
7
+ class EnrichmentRequest:
8
+ """Domain model for generic enrichment request with custom prompt."""
9
+
10
+ id: str
11
+ text: str
12
+ system_prompt: str
@@ -0,0 +1,11 @@
1
+ """Generic enrichment response value object."""
2
+
3
+ from dataclasses import dataclass
4
+
5
+
6
+ @dataclass
7
+ class EnrichmentResponse:
8
+ """Domain model for generic enrichment response."""
9
+
10
+ id: str
11
+ text: str
@@ -0,0 +1 @@
1
+ """Usage enrichment domain entities."""
@@ -0,0 +1,19 @@
1
+ """API documentation enrichment entity."""
2
+
3
+ from dataclasses import dataclass
4
+
5
+ from kodit.domain.enrichments.usage.usage import UsageEnrichment
6
+
7
+ ENRICHMENT_SUBTYPE_API_DOCS = "api_docs"
8
+
9
+
10
+ @dataclass
11
+ class APIDocEnrichment(UsageEnrichment):
12
+ """API documentation enrichment for a module."""
13
+
14
+ language: str = ""
15
+
16
+ @property
17
+ def subtype(self) -> str | None:
18
+ """Return the enrichment subtype."""
19
+ return ENRICHMENT_SUBTYPE_API_DOCS
@@ -0,0 +1,18 @@
1
+ """Usage enrichment domain entity."""
2
+
3
+ from abc import ABC
4
+ from dataclasses import dataclass
5
+
6
+ from kodit.domain.enrichments.enrichment import CommitEnrichment
7
+
8
+ ENRICHMENT_TYPE_USAGE = "usage"
9
+
10
+
11
+ @dataclass
12
+ class UsageEnrichment(CommitEnrichment, ABC):
13
+ """Enrichment containing development discovery for a commit."""
14
+
15
+ @property
16
+ def type(self) -> str:
17
+ """Return the enrichment type."""
18
+ return ENRICHMENT_TYPE_USAGE