kodit 0.1.7__py3-none-any.whl → 0.1.9__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/cli.py +0 -6
- kodit/indexing/repository.py +23 -1
- kodit/indexing/service.py +7 -1
- kodit/sources/service.py +95 -31
- {kodit-0.1.7.dist-info → kodit-0.1.9.dist-info}/METADATA +2 -1
- {kodit-0.1.7.dist-info → kodit-0.1.9.dist-info}/RECORD +10 -10
- {kodit-0.1.7.dist-info → kodit-0.1.9.dist-info}/WHEEL +0 -0
- {kodit-0.1.7.dist-info → kodit-0.1.9.dist-info}/entry_points.txt +0 -0
- {kodit-0.1.7.dist-info → kodit-0.1.9.dist-info}/licenses/LICENSE +0 -0
kodit/_version.py
CHANGED
kodit/cli.py
CHANGED
|
@@ -123,12 +123,6 @@ async def index(
|
|
|
123
123
|
return
|
|
124
124
|
# Handle source indexing
|
|
125
125
|
for source in sources:
|
|
126
|
-
if source.startswith("https://"):
|
|
127
|
-
msg = "Web or git indexing is not implemented yet"
|
|
128
|
-
raise click.UsageError(msg)
|
|
129
|
-
if source.startswith("git"):
|
|
130
|
-
msg = "Git indexing is not implemented yet"
|
|
131
|
-
raise click.UsageError(msg)
|
|
132
126
|
if Path(source).is_file():
|
|
133
127
|
msg = "File indexing is not implemented yet"
|
|
134
128
|
raise click.UsageError(msg)
|
kodit/indexing/repository.py
CHANGED
|
@@ -8,7 +8,7 @@ and retrieving index information with their associated metadata.
|
|
|
8
8
|
from datetime import UTC, datetime
|
|
9
9
|
from typing import TypeVar
|
|
10
10
|
|
|
11
|
-
from sqlalchemy import func, select
|
|
11
|
+
from sqlalchemy import delete, func, select
|
|
12
12
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
13
13
|
|
|
14
14
|
from kodit.indexing.models import Index, Snippet
|
|
@@ -63,6 +63,17 @@ class IndexRepository:
|
|
|
63
63
|
result = await self.session.execute(query)
|
|
64
64
|
return result.scalar_one_or_none()
|
|
65
65
|
|
|
66
|
+
async def get_by_source_id(self, source_id: int) -> Index | None:
|
|
67
|
+
"""Get an index by its source ID.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
source_id: The ID of the source to retrieve an index for.
|
|
71
|
+
|
|
72
|
+
"""
|
|
73
|
+
query = select(Index).where(Index.source_id == source_id)
|
|
74
|
+
result = await self.session.execute(query)
|
|
75
|
+
return result.scalar_one_or_none()
|
|
76
|
+
|
|
66
77
|
async def files_for_index(self, index_id: int) -> list[File]:
|
|
67
78
|
"""Get all files for an index.
|
|
68
79
|
|
|
@@ -122,6 +133,17 @@ class IndexRepository:
|
|
|
122
133
|
self.session.add(snippet)
|
|
123
134
|
await self.session.commit()
|
|
124
135
|
|
|
136
|
+
async def delete_all_snippets(self, index_id: int) -> None:
|
|
137
|
+
"""Delete all snippets for an index.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
index_id: The ID of the index to delete snippets for.
|
|
141
|
+
|
|
142
|
+
"""
|
|
143
|
+
query = delete(Snippet).where(Snippet.index_id == index_id)
|
|
144
|
+
await self.session.execute(query)
|
|
145
|
+
await self.session.commit()
|
|
146
|
+
|
|
125
147
|
async def get_snippets_for_index(self, index_id: int) -> list[Snippet]:
|
|
126
148
|
"""Get all snippets for an index.
|
|
127
149
|
|
kodit/indexing/service.py
CHANGED
|
@@ -83,7 +83,10 @@ class IndexService:
|
|
|
83
83
|
# Check if the source exists
|
|
84
84
|
source = await self.source_service.get(source_id)
|
|
85
85
|
|
|
86
|
-
index
|
|
86
|
+
# Check if the index already exists
|
|
87
|
+
index = await self.repository.get_by_source_id(source.id)
|
|
88
|
+
if not index:
|
|
89
|
+
index = await self.repository.create(source.id)
|
|
87
90
|
return IndexView(
|
|
88
91
|
id=index.id,
|
|
89
92
|
created_at=index.created_at,
|
|
@@ -119,6 +122,9 @@ class IndexService:
|
|
|
119
122
|
msg = f"Index not found: {index_id}"
|
|
120
123
|
raise ValueError(msg)
|
|
121
124
|
|
|
125
|
+
# First delete all old snippets, if they exist
|
|
126
|
+
await self.repository.delete_all_snippets(index_id)
|
|
127
|
+
|
|
122
128
|
# Create snippets for supported file types
|
|
123
129
|
await self._create_snippets(index_id)
|
|
124
130
|
|
kodit/sources/service.py
CHANGED
|
@@ -13,6 +13,7 @@ from hashlib import sha256
|
|
|
13
13
|
from pathlib import Path
|
|
14
14
|
|
|
15
15
|
import aiofiles
|
|
16
|
+
import git
|
|
16
17
|
import pydantic
|
|
17
18
|
import structlog
|
|
18
19
|
from tqdm import tqdm
|
|
@@ -98,8 +99,19 @@ class SourceService:
|
|
|
98
99
|
parsed = urisplit(uri_or_path_like)
|
|
99
100
|
if parsed.scheme == "file":
|
|
100
101
|
return await self._create_folder_source(Path(parsed.path))
|
|
101
|
-
|
|
102
|
-
|
|
102
|
+
if parsed.scheme in ("git", "http", "https") and parsed.path.endswith(
|
|
103
|
+
".git"
|
|
104
|
+
):
|
|
105
|
+
return await self._create_git_source(uri_or_path_like)
|
|
106
|
+
|
|
107
|
+
# Try adding a .git suffix, sometimes people just pass the url
|
|
108
|
+
if not uri_or_path_like.endswith(".git"):
|
|
109
|
+
uri_or_path_like = uri_or_path_like + ".git"
|
|
110
|
+
try:
|
|
111
|
+
return await self._create_git_source(uri_or_path_like)
|
|
112
|
+
except ValueError:
|
|
113
|
+
pass
|
|
114
|
+
|
|
103
115
|
msg = f"Unsupported source type: {uri_or_path_like}"
|
|
104
116
|
raise ValueError(msg)
|
|
105
117
|
|
|
@@ -110,46 +122,98 @@ class SourceService:
|
|
|
110
122
|
directory: The path to the local directory.
|
|
111
123
|
|
|
112
124
|
Raises:
|
|
113
|
-
ValueError: If the folder doesn't exist
|
|
125
|
+
ValueError: If the folder doesn't exist.
|
|
126
|
+
SourceAlreadyExistsError: If the folder is already added.
|
|
114
127
|
|
|
115
128
|
"""
|
|
116
129
|
# Resolve the directory to an absolute path
|
|
117
130
|
directory = directory.expanduser().resolve()
|
|
118
131
|
|
|
119
|
-
|
|
120
|
-
if
|
|
121
|
-
|
|
122
|
-
|
|
132
|
+
source = await self.repository.get_source_by_uri(directory.as_uri())
|
|
133
|
+
if source:
|
|
134
|
+
self.log.info("Source already exists, reusing...", source_id=source.id)
|
|
135
|
+
else:
|
|
136
|
+
# Check if the folder exists
|
|
137
|
+
if not directory.exists():
|
|
138
|
+
msg = f"Folder does not exist: {directory}"
|
|
139
|
+
raise ValueError(msg)
|
|
140
|
+
|
|
141
|
+
# Check if the folder is already added
|
|
142
|
+
if await self.repository.get_source_by_uri(directory.as_uri()):
|
|
143
|
+
msg = f"Directory already added: {directory}"
|
|
144
|
+
raise ValueError(msg)
|
|
145
|
+
|
|
146
|
+
# Clone into a local directory
|
|
147
|
+
clone_path = self.clone_dir / directory.as_posix().replace("/", "_")
|
|
148
|
+
clone_path.mkdir(parents=True, exist_ok=True)
|
|
149
|
+
|
|
150
|
+
# Copy all files recursively, preserving directory structure, ignoring
|
|
151
|
+
# hidden files
|
|
152
|
+
shutil.copytree(
|
|
153
|
+
directory,
|
|
154
|
+
clone_path,
|
|
155
|
+
ignore=shutil.ignore_patterns(".*"),
|
|
156
|
+
dirs_exist_ok=True,
|
|
157
|
+
)
|
|
123
158
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
raise ValueError(msg)
|
|
159
|
+
source = await self.repository.create_source(
|
|
160
|
+
Source(uri=directory.as_uri(), cloned_path=str(clone_path)),
|
|
161
|
+
)
|
|
128
162
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
directory,
|
|
137
|
-
clone_path,
|
|
138
|
-
ignore=shutil.ignore_patterns(".*"),
|
|
139
|
-
dirs_exist_ok=True,
|
|
140
|
-
)
|
|
163
|
+
# Add all files to the source
|
|
164
|
+
# Count total files for progress bar
|
|
165
|
+
file_count = sum(1 for _ in clone_path.rglob("*") if _.is_file())
|
|
166
|
+
|
|
167
|
+
# Process each file in the source directory
|
|
168
|
+
for path in tqdm(clone_path.rglob("*"), total=file_count):
|
|
169
|
+
await self._process_file(source.id, path.absolute())
|
|
141
170
|
|
|
142
|
-
|
|
143
|
-
|
|
171
|
+
return SourceView(
|
|
172
|
+
id=source.id,
|
|
173
|
+
uri=source.uri,
|
|
174
|
+
cloned_path=Path(source.cloned_path),
|
|
175
|
+
created_at=source.created_at,
|
|
176
|
+
num_files=await self.repository.num_files_for_source(source.id),
|
|
144
177
|
)
|
|
145
178
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
179
|
+
async def _create_git_source(self, uri: str) -> SourceView:
|
|
180
|
+
"""Create a git source.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
uri: The URI of the git repository.
|
|
184
|
+
|
|
185
|
+
Raises:
|
|
186
|
+
ValueError: If the repository cloning fails.
|
|
187
|
+
|
|
188
|
+
"""
|
|
189
|
+
# Check if the repository is already added
|
|
190
|
+
source = await self.repository.get_source_by_uri(uri)
|
|
191
|
+
|
|
192
|
+
if source:
|
|
193
|
+
self.log.info("Source already exists, reusing...", source_id=source.id)
|
|
194
|
+
else:
|
|
195
|
+
# Create a unique directory name for the clone
|
|
196
|
+
clone_path = self.clone_dir / uri.replace("/", "_").replace(":", "_")
|
|
197
|
+
clone_path.mkdir(parents=True, exist_ok=True)
|
|
198
|
+
|
|
199
|
+
try:
|
|
200
|
+
# Clone the repository
|
|
201
|
+
git.Repo.clone_from(uri, clone_path)
|
|
202
|
+
except git.GitCommandError as e:
|
|
203
|
+
msg = f"Failed to clone repository: {e}"
|
|
204
|
+
raise ValueError(msg) from e
|
|
205
|
+
|
|
206
|
+
source = await self.repository.create_source(
|
|
207
|
+
Source(uri=uri, cloned_path=str(clone_path)),
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
# Add all files to the source
|
|
211
|
+
# Count total files for progress bar
|
|
212
|
+
file_count = sum(1 for _ in clone_path.rglob("*") if _.is_file())
|
|
149
213
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
214
|
+
# Process each file in the source directory
|
|
215
|
+
for path in tqdm(clone_path.rglob("*"), total=file_count):
|
|
216
|
+
await self._process_file(source.id, path.absolute())
|
|
153
217
|
|
|
154
218
|
return SourceView(
|
|
155
219
|
id=source.id,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kodit
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.9
|
|
4
4
|
Summary: Code indexing for better AI code generation
|
|
5
5
|
Project-URL: Homepage, https://docs.helixml.tech/kodit/
|
|
6
6
|
Project-URL: Documentation, https://docs.helixml.tech/kodit/
|
|
@@ -28,6 +28,7 @@ Requires-Dist: colorama>=0.4.6
|
|
|
28
28
|
Requires-Dist: dotenv>=0.9.9
|
|
29
29
|
Requires-Dist: fastapi[standard]>=0.115.12
|
|
30
30
|
Requires-Dist: fastmcp>=2.3.3
|
|
31
|
+
Requires-Dist: gitpython>=3.1.44
|
|
31
32
|
Requires-Dist: httpx-retries>=0.3.2
|
|
32
33
|
Requires-Dist: httpx>=0.28.1
|
|
33
34
|
Requires-Dist: posthog>=4.0.1
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
kodit/.gitignore,sha256=ztkjgRwL9Uud1OEi36hGQeDGk3OLK1NfDEO8YqGYy8o,11
|
|
2
2
|
kodit/__init__.py,sha256=aEKHYninUq1yh6jaNfvJBYg-6fenpN132nJt1UU6Jxs,59
|
|
3
|
-
kodit/_version.py,sha256=
|
|
3
|
+
kodit/_version.py,sha256=bhntibG3PKk5Ai3XlSNEV8gj-ffItuKloY6vzWn6swo,511
|
|
4
4
|
kodit/app.py,sha256=Mr5BFHOHx5zppwjC4XPWVvHjwgl1yrKbUjTWXKubJQM,891
|
|
5
|
-
kodit/cli.py,sha256=
|
|
5
|
+
kodit/cli.py,sha256=bsfURvGKZzpHkChnTlatI0nXHV3KV_6vJnUJ2fQEAfM,6637
|
|
6
6
|
kodit/config.py,sha256=nlm9U-nVx5riH2SrU1XY4XcCMhQK4DrwO_1H8bPOBjA,2927
|
|
7
7
|
kodit/database.py,sha256=vtTlmrXHyHJH3Ek-twZTCqEjB0jun-NncALFze2fqhA,2350
|
|
8
8
|
kodit/logging.py,sha256=cFEQXWI27LzWScSxly9ApwkbBDamUG17pA-jEfVakXQ,5316
|
|
@@ -18,8 +18,8 @@ kodit/bm25/__init__.py,sha256=j8zyriNWhbwE5Lbybzg1hQAhANlU9mKHWw4beeUR6og,19
|
|
|
18
18
|
kodit/bm25/bm25.py,sha256=3wyNRSrTaYqV7s4R1D6X0NpCf22PuFK2_uc8YapzYLE,2263
|
|
19
19
|
kodit/indexing/__init__.py,sha256=cPyi2Iej3G1JFWlWr7X80_UrsMaTu5W5rBwgif1B3xo,75
|
|
20
20
|
kodit/indexing/models.py,sha256=sZIhGwvL4Dw0QTWFxrjfWctSLkAoDT6fv5DlGz8-Fr8,1258
|
|
21
|
-
kodit/indexing/repository.py,sha256=
|
|
22
|
-
kodit/indexing/service.py,sha256=
|
|
21
|
+
kodit/indexing/repository.py,sha256=ZicLPXPKQxW6NnY_anmZ4nI1-FGkrJsqjg0NK-vvnTY,5117
|
|
22
|
+
kodit/indexing/service.py,sha256=rLWYI70VytlJAyZtQC5Xpqtj9f3EzbivzgeM_1L9BUU,5751
|
|
23
23
|
kodit/retreival/__init__.py,sha256=33PhJU-3gtsqYq6A1UkaLNKbev_Zee9Lq6dYC59-CsA,69
|
|
24
24
|
kodit/retreival/repository.py,sha256=1lqGgJHsBmvMGMzEYa-hrdXg2q7rqtYPl1cvBb7jMRE,3119
|
|
25
25
|
kodit/retreival/service.py,sha256=9wvURtPPJVvPUWNIC2waIrJMxcm1Ka1J_xDEOEedAFU,2007
|
|
@@ -32,9 +32,9 @@ kodit/snippets/languages/python.scm,sha256=ee85R9PBzwye3IMTE7-iVoKWd_ViU3EJISTyr
|
|
|
32
32
|
kodit/sources/__init__.py,sha256=1NTZyPdjThVQpZO1Mp1ColVsS7sqYanOVLqnoqV9Ipo,83
|
|
33
33
|
kodit/sources/models.py,sha256=xb42CaNDO1CUB8SIW-xXMrB6Ji8cFw-yeJ550xBEg9Q,2398
|
|
34
34
|
kodit/sources/repository.py,sha256=mGJrHWH6Uo8YABdoojHFbzaf_jW-2ywJpAHIa1gnc3U,3401
|
|
35
|
-
kodit/sources/service.py,sha256=
|
|
36
|
-
kodit-0.1.
|
|
37
|
-
kodit-0.1.
|
|
38
|
-
kodit-0.1.
|
|
39
|
-
kodit-0.1.
|
|
40
|
-
kodit-0.1.
|
|
35
|
+
kodit/sources/service.py,sha256=hqAjGFVhvtePhMrK1Aprj__Mq2PLjVq8CsWMBoA3_Qw,9217
|
|
36
|
+
kodit-0.1.9.dist-info/METADATA,sha256=MAqVxrLPrTV3Ihcix_3YHQNq9qyuD1OEavYHV76qli8,2214
|
|
37
|
+
kodit-0.1.9.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
38
|
+
kodit-0.1.9.dist-info/entry_points.txt,sha256=hoTn-1aKyTItjnY91fnO-rV5uaWQLQ-Vi7V5et2IbHY,40
|
|
39
|
+
kodit-0.1.9.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
40
|
+
kodit-0.1.9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|