kodit 0.2.9__py3-none-any.whl → 0.3.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 CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.2.9'
21
- __version_tuple__ = version_tuple = (0, 2, 9)
20
+ __version__ = version = '0.3.0'
21
+ __version_tuple__ = version_tuple = (0, 3, 0)
kodit/app.py CHANGED
@@ -1,14 +1,49 @@
1
1
  """FastAPI application for kodit API."""
2
2
 
3
+ from collections.abc import AsyncIterator
4
+ from contextlib import asynccontextmanager
5
+
3
6
  from asgi_correlation_id import CorrelationIdMiddleware
4
7
  from fastapi import FastAPI
5
8
 
9
+ from kodit.config import AppContext
10
+ from kodit.infrastructure.indexing.auto_indexing_service import AutoIndexingService
6
11
  from kodit.mcp import mcp
7
12
  from kodit.middleware import ASGICancelledErrorMiddleware, logging_middleware
8
13
 
14
+ # Global auto-indexing service
15
+ _auto_indexing_service: AutoIndexingService | None = None
16
+
17
+
18
+ @asynccontextmanager
19
+ async def app_lifespan(_: FastAPI) -> AsyncIterator[None]:
20
+ """Manage application lifespan for auto-indexing."""
21
+ global _auto_indexing_service # noqa: PLW0603
22
+ # Start auto-indexing service
23
+ app_context = AppContext()
24
+ db = await app_context.get_db()
25
+ _auto_indexing_service = AutoIndexingService(
26
+ app_context=app_context,
27
+ session_factory=db.session_factory,
28
+ )
29
+ await _auto_indexing_service.start_background_indexing()
30
+ yield
31
+ if _auto_indexing_service:
32
+ await _auto_indexing_service.stop()
33
+
34
+
9
35
  # See https://gofastmcp.com/deployment/asgi#fastapi-integration
10
36
  mcp_app = mcp.sse_app()
11
- app = FastAPI(title="kodit API", lifespan=mcp_app.router.lifespan_context)
37
+
38
+
39
+ @asynccontextmanager
40
+ async def combined_lifespan(app: FastAPI) -> AsyncIterator[None]:
41
+ """Combine app and MCP lifespans."""
42
+ async with app_lifespan(app), mcp_app.router.lifespan_context(app):
43
+ yield
44
+
45
+
46
+ app = FastAPI(title="kodit API", lifespan=combined_lifespan)
12
47
 
13
48
  # Add middleware
14
49
  app.middleware("http")(logging_middleware)
kodit/cli.py CHANGED
@@ -59,12 +59,17 @@ def cli(
59
59
 
60
60
  @cli.command()
61
61
  @click.argument("sources", nargs=-1)
62
+ @click.option(
63
+ "--auto-index", is_flag=True, help="Index all configured auto-index sources"
64
+ )
62
65
  @with_app_context
63
66
  @with_session
64
67
  async def index(
65
68
  session: AsyncSession,
66
69
  app_context: AppContext,
67
70
  sources: list[str],
71
+ *, # Force keyword-only arguments
72
+ auto_index: bool,
68
73
  ) -> None:
69
74
  """List indexes, or index data sources."""
70
75
  log = structlog.get_logger(__name__)
@@ -78,6 +83,16 @@ async def index(
78
83
  source_service=source_service,
79
84
  )
80
85
 
86
+ if auto_index:
87
+ log.info("Auto-indexing configuration", config=app_context.auto_indexing)
88
+ auto_sources = app_context.auto_indexing.sources
89
+ if not auto_sources:
90
+ click.echo("No auto-index sources configured.")
91
+ return
92
+
93
+ click.echo(f"Auto-indexing {len(auto_sources)} configured sources...")
94
+ sources = [source.uri for source in auto_sources]
95
+
81
96
  if not sources:
82
97
  log_event("kodit.cli.index.list")
83
98
  # No source specified, list all indexes
kodit/config.py CHANGED
@@ -8,7 +8,7 @@ from pathlib import Path
8
8
  from typing import TYPE_CHECKING, Any, Literal, TypeVar
9
9
 
10
10
  import click
11
- from pydantic import BaseModel, Field
11
+ from pydantic import BaseModel, Field, field_validator
12
12
  from pydantic_settings import BaseSettings, SettingsConfigDict
13
13
 
14
14
  if TYPE_CHECKING:
@@ -37,11 +37,45 @@ class Endpoint(BaseModel):
37
37
 
38
38
 
39
39
  class Search(BaseModel):
40
- """Search provides configuration for a search engine."""
40
+ """Search configuration."""
41
41
 
42
42
  provider: Literal["sqlite", "vectorchord"] = Field(default="sqlite")
43
43
 
44
44
 
45
+ class AutoIndexingSource(BaseModel):
46
+ """Configuration for a single auto-indexing source."""
47
+
48
+ uri: str = Field(description="URI of the source to index (git URL or local path)")
49
+
50
+
51
+ class AutoIndexingConfig(BaseModel):
52
+ """Configuration for auto-indexing."""
53
+
54
+ sources: list[AutoIndexingSource] = Field(
55
+ default_factory=list, description="List of sources to auto-index"
56
+ )
57
+
58
+ @field_validator("sources", mode="before")
59
+ @classmethod
60
+ def parse_sources(cls, v: Any) -> list[AutoIndexingSource]:
61
+ """Parse sources from environment variables or other formats."""
62
+ if v is None:
63
+ return []
64
+ if isinstance(v, list):
65
+ return v
66
+ if isinstance(v, dict):
67
+ # Handle case where env vars are numbered keys like {'0': {'uri': '...'}}
68
+ sources = []
69
+ i = 0
70
+ while str(i) in v:
71
+ source_data = v[str(i)]
72
+ if isinstance(source_data, dict) and "uri" in source_data:
73
+ sources.append(AutoIndexingSource(uri=source_data["uri"]))
74
+ i += 1
75
+ return sources
76
+ return v
77
+
78
+
45
79
  class AppContext(BaseSettings):
46
80
  """Global context for the kodit project. Provides a shared state for the app."""
47
81
 
@@ -50,7 +84,7 @@ class AppContext(BaseSettings):
50
84
  env_file_encoding="utf-8",
51
85
  env_nested_delimiter="_",
52
86
  nested_model_default_partial_update=True,
53
- env_nested_max_split=1,
87
+ extra="ignore",
54
88
  )
55
89
 
56
90
  data_dir: Path = Field(default=DEFAULT_BASE_DIR)
@@ -76,6 +110,9 @@ class AppContext(BaseSettings):
76
110
  default_search: Search = Field(
77
111
  default=Search(),
78
112
  )
113
+ auto_indexing: AutoIndexingConfig | None = Field(
114
+ default=AutoIndexingConfig(), description="Auto-indexing configuration"
115
+ )
79
116
  _db: Database | None = None
80
117
 
81
118
  def model_post_init(self, _: Any) -> None:
@@ -0,0 +1,84 @@
1
+ """Service for automatically indexing configured sources."""
2
+
3
+ import asyncio
4
+ from collections.abc import Callable
5
+ from contextlib import suppress
6
+
7
+ import structlog
8
+ from sqlalchemy.ext.asyncio import AsyncSession
9
+
10
+ from kodit.application.factories.code_indexing_factory import (
11
+ create_code_indexing_application_service,
12
+ )
13
+ from kodit.config import AppContext
14
+ from kodit.domain.services.source_service import SourceService
15
+
16
+
17
+ class AutoIndexingService:
18
+ """Service for automatically indexing configured sources."""
19
+
20
+ def __init__(
21
+ self,
22
+ app_context: AppContext,
23
+ session_factory: Callable[[], AsyncSession],
24
+ ) -> None:
25
+ """Initialize the auto-indexing service."""
26
+ self.app_context = app_context
27
+ self.session_factory = session_factory
28
+ self.log = structlog.get_logger(__name__)
29
+ self._indexing_task: asyncio.Task | None = None
30
+
31
+ async def start_background_indexing(self) -> None:
32
+ """Start background indexing of configured sources."""
33
+ if (
34
+ not self.app_context.auto_indexing
35
+ or len(self.app_context.auto_indexing.sources) == 0
36
+ ):
37
+ self.log.info("Auto-indexing is disabled (no sources configured)")
38
+ return
39
+
40
+ auto_sources = [source.uri for source in self.app_context.auto_indexing.sources]
41
+ self.log.info("Starting background indexing", num_sources=len(auto_sources))
42
+ self._indexing_task = asyncio.create_task(self._index_sources(auto_sources))
43
+
44
+ async def _index_sources(self, sources: list[str]) -> None:
45
+ """Index all configured sources in the background."""
46
+ async with self.session_factory() as session:
47
+ source_service = SourceService(
48
+ clone_dir=self.app_context.get_clone_dir(),
49
+ session_factory=lambda: session,
50
+ )
51
+
52
+ service = create_code_indexing_application_service(
53
+ app_context=self.app_context,
54
+ session=session,
55
+ source_service=source_service,
56
+ )
57
+
58
+ for source in sources:
59
+ try:
60
+ self.log.info("Auto-indexing source", source=source)
61
+
62
+ # Create source
63
+ s = await source_service.create(source)
64
+
65
+ # Create index
66
+ index = await service.create_index(s.id)
67
+
68
+ # Run indexing (without progress callback for background mode)
69
+ await service.run_index(index.id, progress_callback=None)
70
+
71
+ self.log.info("Successfully auto-indexed source", source=source)
72
+
73
+ except Exception as exc:
74
+ self.log.exception(
75
+ "Failed to auto-index source", source=source, error=str(exc)
76
+ )
77
+ # Continue with other sources even if one fails
78
+
79
+ async def stop(self) -> None:
80
+ """Stop background indexing."""
81
+ if self._indexing_task:
82
+ self._indexing_task.cancel()
83
+ with suppress(asyncio.CancelledError):
84
+ await self._indexing_task
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kodit
3
- Version: 0.2.9
3
+ Version: 0.3.0
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/
@@ -1,9 +1,9 @@
1
1
  kodit/.gitignore,sha256=ztkjgRwL9Uud1OEi36hGQeDGk3OLK1NfDEO8YqGYy8o,11
2
2
  kodit/__init__.py,sha256=aEKHYninUq1yh6jaNfvJBYg-6fenpN132nJt1UU6Jxs,59
3
- kodit/_version.py,sha256=Iq6CyehddPOWDVsW9Hnb65BEkCEkAnt4bl0MAuqXKLA,511
4
- kodit/app.py,sha256=qKBWJ0VNSY_M6G3VFfAQ0133q5bnS99cUFD0p396taw,1032
5
- kodit/cli.py,sha256=BbJU4ne5bQnl3NCB2AWr_j9E7pTwc1dK_2oHkbnGbBU,16300
6
- kodit/config.py,sha256=3yh7hfLSILjZK_qJMhcExwRcrWJ0b5Eb1JjjOvMPJZo,4146
3
+ kodit/_version.py,sha256=AGmG_Lx0-9ztFw_7d9mYbaYuC-2abxE1oXOUNAY29YY,511
4
+ kodit/app.py,sha256=uv67TE83fZE7wrA7cz-sKosFrAXlKRr1B7fT-X_gMZQ,2103
5
+ kodit/cli.py,sha256=ihecBP7Og0F-wXYZ63TMeYzuqgFpfd7O-g4NRB3pufo,16864
6
+ kodit/config.py,sha256=myWtVujjd7xQh3tPK_zGC1L5D5PYoPknYvUBoMLJ1kM,5414
7
7
  kodit/database.py,sha256=kI9yBm4uunsgV4-QeVoCBL0wLzU4kYmYv5qZilGnbPE,1740
8
8
  kodit/log.py,sha256=sHPHYetlMcKTor2VaFLMyao1_fZ_xhuzqXCAt5F5UMU,8575
9
9
  kodit/mcp.py,sha256=onmUbZ1-mnBkQOUF6KymNZMiTo19tt6pDOmyBQEz8Jg,6454
@@ -62,6 +62,7 @@ kodit/infrastructure/git/git_utils.py,sha256=2DH6cyTjDRwFfL5Bzt1y2w0DwHZNypbC6R0
62
62
  kodit/infrastructure/ignore/__init__.py,sha256=VzFv8XOzHmsu0MEGnWVSF6KsgqLBmvHlRqAkT1Xb1MY,36
63
63
  kodit/infrastructure/ignore/ignore_pattern_provider.py,sha256=9m2XCsgW87UBTfzHr6Z0Ns6WpzwkLir3zyBY3PwsgXk,2225
64
64
  kodit/infrastructure/indexing/__init__.py,sha256=7UPRa2jwCAsa0Orsp6PqXSF8iIXJVzXHMFmrKkI9yH8,38
65
+ kodit/infrastructure/indexing/auto_indexing_service.py,sha256=uXggladN3PTU5Jzhz0Kq-0aObvq3Dq9YbjYKCSkaQA8,3131
65
66
  kodit/infrastructure/indexing/fusion_service.py,sha256=mXUUcx3-8e75mWkxXMfl30HIoFXrTNHzB1w90MmEbak,1806
66
67
  kodit/infrastructure/indexing/index_repository.py,sha256=4aSCBE_Gn9ihOx_kXOpUTTIv6_Q71-VRFHEBgpWaAEw,8906
67
68
  kodit/infrastructure/indexing/indexing_factory.py,sha256=LPjPCps_wJ9M_fZGRP02bfc2pvYc50ZSTYI99XwRRPg,918
@@ -94,8 +95,8 @@ kodit/migrations/versions/85155663351e_initial.py,sha256=Cg7zlF871o9ShV5rQMQ1v7h
94
95
  kodit/migrations/versions/9e53ea8bb3b0_add_authors.py,sha256=a32Zm8KUQyiiLkjKNPYdaJDgjW6VsV-GhaLnPnK_fpI,3884
95
96
  kodit/migrations/versions/__init__.py,sha256=9-lHzptItTzq_fomdIRBegQNm4Znx6pVjwD4MiqRIdo,36
96
97
  kodit/migrations/versions/c3f5137d30f5_index_all_the_things.py,sha256=rI8LmjF-I2OMxZ2nOIF_NRmqOLXe45hL_iz_nx97DTQ,1680
97
- kodit-0.2.9.dist-info/METADATA,sha256=s454eRkiLIe50_PWl9sODi3_9NGtt-Q6pFNofPywwLA,5867
98
- kodit-0.2.9.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
99
- kodit-0.2.9.dist-info/entry_points.txt,sha256=hoTn-1aKyTItjnY91fnO-rV5uaWQLQ-Vi7V5et2IbHY,40
100
- kodit-0.2.9.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
101
- kodit-0.2.9.dist-info/RECORD,,
98
+ kodit-0.3.0.dist-info/METADATA,sha256=7VZ989c731WalePCDWB1KSIUKJ9vS5hcgYVuI2Souiw,5867
99
+ kodit-0.3.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
100
+ kodit-0.3.0.dist-info/entry_points.txt,sha256=hoTn-1aKyTItjnY91fnO-rV5uaWQLQ-Vi7V5et2IbHY,40
101
+ kodit-0.3.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
102
+ kodit-0.3.0.dist-info/RECORD,,
File without changes