agent-coderag 1.3.2__tar.gz → 1.3.3__tar.gz
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.
- {agent_coderag-1.3.2/agent_coderag.egg-info → agent_coderag-1.3.3}/PKG-INFO +17 -1
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/README.md +15 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3/agent_coderag.egg-info}/PKG-INFO +17 -1
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/agent_coderag.egg-info/SOURCES.txt +14 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/agent_coderag.egg-info/requires.txt +1 -0
- agent_coderag-1.3.3/code_rag/__init__.py +16 -0
- agent_coderag-1.3.3/code_rag/api/__init__.py +6 -0
- agent_coderag-1.3.3/code_rag/api/client.py +87 -0
- agent_coderag-1.3.3/code_rag/api/models.py +20 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/code_rag/core/manager.py +1 -1
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/code_rag/entry/cli.py +69 -103
- agent_coderag-1.3.3/code_rag/services/config.py +25 -0
- agent_coderag-1.3.3/code_rag/services/discovery_api.py +8 -0
- agent_coderag-1.3.3/code_rag/services/factory.py +28 -0
- agent_coderag-1.3.3/code_rag/services/search.py +6 -0
- agent_coderag-1.3.3/code_rag/services/setup.py +72 -0
- agent_coderag-1.3.3/code_rag/services/sync.py +101 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/pyproject.toml +2 -1
- agent_coderag-1.3.3/tests/test_api_client.py +186 -0
- agent_coderag-1.3.3/tests/test_api_models.py +25 -0
- agent_coderag-1.3.3/tests/test_cli_json_parity.py +100 -0
- agent_coderag-1.3.3/tests/test_readme_library_usage.py +8 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/LICENSE +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/NOTICE +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/agent_coderag.egg-info/dependency_links.txt +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/agent_coderag.egg-info/entry_points.txt +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/agent_coderag.egg-info/top_level.txt +0 -0
- {agent_coderag-1.3.2/code_rag → agent_coderag-1.3.3/code_rag/core}/__init__.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/code_rag/core/constants.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/code_rag/core/exceptions.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/code_rag/core/interfaces.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/code_rag/core/models.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/code_rag/core/utils.py +0 -0
- {agent_coderag-1.3.2/code_rag/core → agent_coderag-1.3.3/code_rag/discovery}/__init__.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/code_rag/discovery/dependency.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/code_rag/discovery/java_discovery.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/code_rag/discovery/manager.py +0 -0
- {agent_coderag-1.3.2/code_rag/discovery → agent_coderag-1.3.3/code_rag/discovery/providers}/__init__.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/code_rag/discovery/providers/base.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/code_rag/discovery/providers/csharp.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/code_rag/discovery/providers/go.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/code_rag/discovery/providers/java.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/code_rag/discovery/providers/javascript.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/code_rag/discovery/providers/python.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/code_rag/discovery/providers/rust.py +0 -0
- {agent_coderag-1.3.2/code_rag/discovery/providers → agent_coderag-1.3.3/code_rag/entry}/__init__.py +0 -0
- {agent_coderag-1.3.2/code_rag/entry → agent_coderag-1.3.3/code_rag/intelligence}/__init__.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/code_rag/intelligence/distiller.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/code_rag/intelligence/embedder.py +0 -0
- {agent_coderag-1.3.2/code_rag/intelligence → agent_coderag-1.3.3/code_rag/parsers}/__init__.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/code_rag/parsers/languages.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/code_rag/parsers/multi_parser.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/code_rag/parsers/tree_sitter.py +0 -0
- {agent_coderag-1.3.2/code_rag/parsers → agent_coderag-1.3.3/code_rag/services}/__init__.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/code_rag/storage/__init__.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/code_rag/storage/duckdb_impl.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/setup.cfg +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/tests/test_cli.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/tests/test_cli_detailed.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/tests/test_code_rag_simple.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/tests/test_csharp_discovery_detailed.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/tests/test_dependency_discovery.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/tests/test_discovery_manager_detailed.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/tests/test_discovery_providers_extra.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/tests/test_discovery_python.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/tests/test_distiller.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/tests/test_distiller_extra.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/tests/test_embedder.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/tests/test_embedder_detailed.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/tests/test_interfaces.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/tests/test_js_discovery_detailed.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/tests/test_languages.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/tests/test_manager_detailed.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/tests/test_manager_extra.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/tests/test_models.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/tests/test_multi_parser.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/tests/test_rust_discovery_detailed.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/tests/test_storage_detailed.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/tests/test_tree_sitter_parser.py +0 -0
- {agent_coderag-1.3.2 → agent_coderag-1.3.3}/tests/test_utils_detailed.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-coderag
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.3
|
|
4
4
|
Summary: Lightweight semantic code search and distillation utility for AI coding agents. It solves the API knowledge gap via real-time local signature extraction and intent analysis without PyTorch. Optimized for token efficiency, it compresses codebase context into compact semantic summaries stored in a local DuckDB vector similarity index.
|
|
5
5
|
Author-email: Igor Boloban <naranor@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -29,6 +29,7 @@ Requires-Dist: onnxruntime
|
|
|
29
29
|
Requires-Dist: tokenizers
|
|
30
30
|
Requires-Dist: pydantic
|
|
31
31
|
Requires-Dist: httpx
|
|
32
|
+
Requires-Dist: requests
|
|
32
33
|
Requires-Dist: aiofiles
|
|
33
34
|
Requires-Dist: pathspec
|
|
34
35
|
Requires-Dist: tree-sitter
|
|
@@ -148,6 +149,21 @@ agent-coderag api fmt
|
|
|
148
149
|
|
|
149
150
|
---
|
|
150
151
|
|
|
152
|
+
## Library Usage
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
from code_rag import CodeRAG
|
|
156
|
+
|
|
157
|
+
async def main():
|
|
158
|
+
async with CodeRAG(db="code_rag.db") as rag:
|
|
159
|
+
await rag.setup()
|
|
160
|
+
await rag.sync(index_all=True)
|
|
161
|
+
hits = await rag.search("authentication middleware", limit=5)
|
|
162
|
+
report = await rag.api("pydantic", lang="python")
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
151
167
|
## Supported Ecosystems (Discovery)
|
|
152
168
|
|
|
153
169
|
| Language | Method | Discovery Source |
|
|
@@ -103,6 +103,21 @@ agent-coderag api fmt
|
|
|
103
103
|
|
|
104
104
|
---
|
|
105
105
|
|
|
106
|
+
## Library Usage
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
from code_rag import CodeRAG
|
|
110
|
+
|
|
111
|
+
async def main():
|
|
112
|
+
async with CodeRAG(db="code_rag.db") as rag:
|
|
113
|
+
await rag.setup()
|
|
114
|
+
await rag.sync(index_all=True)
|
|
115
|
+
hits = await rag.search("authentication middleware", limit=5)
|
|
116
|
+
report = await rag.api("pydantic", lang="python")
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
106
121
|
## Supported Ecosystems (Discovery)
|
|
107
122
|
|
|
108
123
|
| Language | Method | Discovery Source |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-coderag
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.3
|
|
4
4
|
Summary: Lightweight semantic code search and distillation utility for AI coding agents. It solves the API knowledge gap via real-time local signature extraction and intent analysis without PyTorch. Optimized for token efficiency, it compresses codebase context into compact semantic summaries stored in a local DuckDB vector similarity index.
|
|
5
5
|
Author-email: Igor Boloban <naranor@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -29,6 +29,7 @@ Requires-Dist: onnxruntime
|
|
|
29
29
|
Requires-Dist: tokenizers
|
|
30
30
|
Requires-Dist: pydantic
|
|
31
31
|
Requires-Dist: httpx
|
|
32
|
+
Requires-Dist: requests
|
|
32
33
|
Requires-Dist: aiofiles
|
|
33
34
|
Requires-Dist: pathspec
|
|
34
35
|
Requires-Dist: tree-sitter
|
|
@@ -148,6 +149,21 @@ agent-coderag api fmt
|
|
|
148
149
|
|
|
149
150
|
---
|
|
150
151
|
|
|
152
|
+
## Library Usage
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
from code_rag import CodeRAG
|
|
156
|
+
|
|
157
|
+
async def main():
|
|
158
|
+
async with CodeRAG(db="code_rag.db") as rag:
|
|
159
|
+
await rag.setup()
|
|
160
|
+
await rag.sync(index_all=True)
|
|
161
|
+
hits = await rag.search("authentication middleware", limit=5)
|
|
162
|
+
report = await rag.api("pydantic", lang="python")
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
151
167
|
## Supported Ecosystems (Discovery)
|
|
152
168
|
|
|
153
169
|
| Language | Method | Discovery Source |
|
|
@@ -9,6 +9,9 @@ agent_coderag.egg-info/entry_points.txt
|
|
|
9
9
|
agent_coderag.egg-info/requires.txt
|
|
10
10
|
agent_coderag.egg-info/top_level.txt
|
|
11
11
|
code_rag/__init__.py
|
|
12
|
+
code_rag/api/__init__.py
|
|
13
|
+
code_rag/api/client.py
|
|
14
|
+
code_rag/api/models.py
|
|
12
15
|
code_rag/core/__init__.py
|
|
13
16
|
code_rag/core/constants.py
|
|
14
17
|
code_rag/core/exceptions.py
|
|
@@ -37,10 +40,20 @@ code_rag/parsers/__init__.py
|
|
|
37
40
|
code_rag/parsers/languages.py
|
|
38
41
|
code_rag/parsers/multi_parser.py
|
|
39
42
|
code_rag/parsers/tree_sitter.py
|
|
43
|
+
code_rag/services/__init__.py
|
|
44
|
+
code_rag/services/config.py
|
|
45
|
+
code_rag/services/discovery_api.py
|
|
46
|
+
code_rag/services/factory.py
|
|
47
|
+
code_rag/services/search.py
|
|
48
|
+
code_rag/services/setup.py
|
|
49
|
+
code_rag/services/sync.py
|
|
40
50
|
code_rag/storage/__init__.py
|
|
41
51
|
code_rag/storage/duckdb_impl.py
|
|
52
|
+
tests/test_api_client.py
|
|
53
|
+
tests/test_api_models.py
|
|
42
54
|
tests/test_cli.py
|
|
43
55
|
tests/test_cli_detailed.py
|
|
56
|
+
tests/test_cli_json_parity.py
|
|
44
57
|
tests/test_code_rag_simple.py
|
|
45
58
|
tests/test_csharp_discovery_detailed.py
|
|
46
59
|
tests/test_dependency_discovery.py
|
|
@@ -58,6 +71,7 @@ tests/test_manager_detailed.py
|
|
|
58
71
|
tests/test_manager_extra.py
|
|
59
72
|
tests/test_models.py
|
|
60
73
|
tests/test_multi_parser.py
|
|
74
|
+
tests/test_readme_library_usage.py
|
|
61
75
|
tests/test_rust_discovery_detailed.py
|
|
62
76
|
tests/test_storage_detailed.py
|
|
63
77
|
tests/test_tree_sitter_parser.py
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from code_rag.api.client import CodeRAG
|
|
2
|
+
from code_rag.api.models import SyncResult, ApiReport, SetupResult
|
|
3
|
+
from code_rag.core.models import KnowledgeUnit, UnitKind
|
|
4
|
+
from code_rag.intelligence.distiller import DistillerConfig
|
|
5
|
+
from code_rag.core.exceptions import CodeRAGError
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"CodeRAG",
|
|
9
|
+
"SyncResult",
|
|
10
|
+
"ApiReport",
|
|
11
|
+
"SetupResult",
|
|
12
|
+
"KnowledgeUnit",
|
|
13
|
+
"UnitKind",
|
|
14
|
+
"DistillerConfig",
|
|
15
|
+
"CodeRAGError",
|
|
16
|
+
]
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"""Public API package for CodeRAG.
|
|
2
|
+
|
|
3
|
+
`CodeRAG` lives in `code_rag.api.client` and is re-exported from top-level
|
|
4
|
+
`code_rag`. Import models from `code_rag.api.models` directly; this package
|
|
5
|
+
init stays empty to avoid circular imports with `code_rag.services.*`.
|
|
6
|
+
"""
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Optional, Union
|
|
3
|
+
|
|
4
|
+
from code_rag.api.models import ApiReport, SetupResult, SyncResult
|
|
5
|
+
from code_rag.core.models import KnowledgeUnit
|
|
6
|
+
from code_rag.intelligence.distiller import DistillerConfig
|
|
7
|
+
from code_rag.services.config import load_or_update_config
|
|
8
|
+
from code_rag.services.discovery_api import run_api
|
|
9
|
+
from code_rag.services.factory import create_manager
|
|
10
|
+
from code_rag.services.search import run_search
|
|
11
|
+
from code_rag.services.setup import run_setup
|
|
12
|
+
from code_rag.services.sync import run_rebuild, run_sync
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class CodeRAG:
|
|
16
|
+
"""Public async facade over CodeRAG services and the storage/search manager."""
|
|
17
|
+
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
db: str = "code_rag.db",
|
|
21
|
+
onnx: Optional[str] = None,
|
|
22
|
+
*,
|
|
23
|
+
root: Optional[Union[str, Path]] = None,
|
|
24
|
+
allow_build_execution: bool = False,
|
|
25
|
+
):
|
|
26
|
+
self._db = db
|
|
27
|
+
self._onnx = onnx
|
|
28
|
+
self._root = Path(root) if root is not None else Path.cwd()
|
|
29
|
+
self._allow_build_execution = allow_build_execution
|
|
30
|
+
self._manager = None
|
|
31
|
+
|
|
32
|
+
async def __aenter__(self) -> "CodeRAG":
|
|
33
|
+
return self
|
|
34
|
+
|
|
35
|
+
async def __aexit__(self, *_exc) -> None:
|
|
36
|
+
await self.close()
|
|
37
|
+
|
|
38
|
+
async def close(self) -> None:
|
|
39
|
+
if self._manager is not None:
|
|
40
|
+
await self._manager.close()
|
|
41
|
+
self._manager = None
|
|
42
|
+
|
|
43
|
+
def _ensure_manager(self):
|
|
44
|
+
if self._manager is None:
|
|
45
|
+
self._manager = create_manager(
|
|
46
|
+
self._db, self._onnx, allow_build_execution=self._allow_build_execution
|
|
47
|
+
)
|
|
48
|
+
return self._manager
|
|
49
|
+
|
|
50
|
+
async def config(
|
|
51
|
+
self,
|
|
52
|
+
*,
|
|
53
|
+
url: Optional[str] = None,
|
|
54
|
+
key: Optional[str] = None,
|
|
55
|
+
model: Optional[str] = None,
|
|
56
|
+
provider: Optional[str] = None,
|
|
57
|
+
) -> DistillerConfig:
|
|
58
|
+
return load_or_update_config(url=url, key=key, model=model, provider=provider)
|
|
59
|
+
|
|
60
|
+
async def setup(self, *, force: bool = False) -> SetupResult:
|
|
61
|
+
return await run_setup(force=force)
|
|
62
|
+
|
|
63
|
+
async def sync(
|
|
64
|
+
self,
|
|
65
|
+
path: Optional[str] = None,
|
|
66
|
+
*,
|
|
67
|
+
index_all: bool = False,
|
|
68
|
+
force: bool = False,
|
|
69
|
+
) -> SyncResult:
|
|
70
|
+
if path is None and not index_all:
|
|
71
|
+
return SyncResult(status="success", indexed_files=0)
|
|
72
|
+
return await run_sync(
|
|
73
|
+
self._ensure_manager(),
|
|
74
|
+
root=self._root,
|
|
75
|
+
path=path,
|
|
76
|
+
index_all=index_all,
|
|
77
|
+
force=force,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
async def search(self, query: str, *, limit: int = 5) -> list[KnowledgeUnit]:
|
|
81
|
+
return await run_search(self._ensure_manager(), query, limit=limit)
|
|
82
|
+
|
|
83
|
+
async def api(self, library: str, *, lang: Optional[str] = None) -> ApiReport:
|
|
84
|
+
return await run_api(self._ensure_manager(), library, lang=lang)
|
|
85
|
+
|
|
86
|
+
async def rebuild(self) -> SyncResult:
|
|
87
|
+
return await run_rebuild(self._ensure_manager(), root=self._root)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from pydantic import BaseModel, ConfigDict
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class SyncResult(BaseModel):
|
|
5
|
+
status: str
|
|
6
|
+
indexed_files: int
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ApiReport(BaseModel):
|
|
10
|
+
library: str
|
|
11
|
+
language: str
|
|
12
|
+
report: str
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class SetupResult(BaseModel):
|
|
16
|
+
model_config = ConfigDict(protected_namespaces=())
|
|
17
|
+
|
|
18
|
+
model_dir: str
|
|
19
|
+
downloaded: list[str]
|
|
20
|
+
skipped: list[str]
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import asyncio
|
|
3
|
-
import os
|
|
4
3
|
import argparse
|
|
5
4
|
import sys
|
|
6
5
|
import json
|
|
@@ -15,49 +14,50 @@ from ..storage.duckdb_impl import DuckDBStorage
|
|
|
15
14
|
from ..parsers.multi_parser import MultiParser
|
|
16
15
|
from ..intelligence.distiller import Distiller, DistillerConfig
|
|
17
16
|
from ..intelligence.embedder import Embedder, get_global_dir
|
|
18
|
-
from ..parsers.languages import EXTENSION_TO_LANGUAGE
|
|
19
17
|
from ..core.utils import validate_path
|
|
20
|
-
from ..core.exceptions import CodeRAGError
|
|
18
|
+
from ..core.exceptions import CodeRAGError
|
|
19
|
+
from ..services import sync as sync_service
|
|
20
|
+
from ..services.discovery_api import run_api
|
|
21
|
+
from ..services.search import run_search
|
|
22
|
+
from ..services.setup import run_setup
|
|
21
23
|
|
|
22
24
|
logger = logging.getLogger(__name__)
|
|
23
25
|
|
|
26
|
+
# Legacy compatibility surface: these names are imported/patched by existing
|
|
27
|
+
# integrations and tests, so they must stay resolvable on this module.
|
|
28
|
+
__all__ = [
|
|
29
|
+
"get_manager",
|
|
30
|
+
"load_ignore_patterns",
|
|
31
|
+
"should_index",
|
|
32
|
+
"sync_cmd",
|
|
33
|
+
"search_cmd",
|
|
34
|
+
"api_cmd",
|
|
35
|
+
"config_cmd",
|
|
36
|
+
"setup_cmd",
|
|
37
|
+
"rebuild_cmd",
|
|
38
|
+
"DistillerConfig",
|
|
39
|
+
"Embedder",
|
|
40
|
+
"DuckDBStorage",
|
|
41
|
+
"MultiParser",
|
|
42
|
+
"Distiller",
|
|
43
|
+
"get_global_dir",
|
|
44
|
+
"requests",
|
|
45
|
+
"validate_path",
|
|
46
|
+
"main",
|
|
47
|
+
]
|
|
48
|
+
|
|
24
49
|
|
|
25
50
|
def load_ignore_patterns() -> pathspec.PathSpec:
|
|
26
|
-
"""Loads ignore patterns from .gitignore or defaults."""
|
|
27
|
-
|
|
28
|
-
if os.path.exists(".gitignore"):
|
|
29
|
-
with open(".gitignore", "r", encoding="utf-8") as f:
|
|
30
|
-
lines = f.readlines()
|
|
31
|
-
|
|
32
|
-
common_excludes = [
|
|
33
|
-
"node_modules/",
|
|
34
|
-
"venv/",
|
|
35
|
-
".venv/",
|
|
36
|
-
"__pycache__/",
|
|
37
|
-
".git/",
|
|
38
|
-
".idea/",
|
|
39
|
-
".vscode/",
|
|
40
|
-
".onnx",
|
|
41
|
-
".db",
|
|
42
|
-
".ai/",
|
|
43
|
-
]
|
|
44
|
-
return pathspec.PathSpec.from_lines("gitignore", lines + common_excludes)
|
|
51
|
+
"""Loads ignore patterns from the current directory's .gitignore or defaults."""
|
|
52
|
+
return sync_service.load_ignore_patterns(Path.cwd())
|
|
45
53
|
|
|
46
54
|
|
|
47
55
|
def should_index(path: Path, ignore_spec: Optional[pathspec.PathSpec] = None) -> bool:
|
|
48
56
|
"""Filters files that should NOT be indexed."""
|
|
49
|
-
|
|
50
|
-
path_str = str(path).replace(os.sep, "/")
|
|
51
|
-
|
|
52
|
-
# 1. Check against ignore spec (including .gitignore and common excludes)
|
|
53
|
-
if ignore_spec and ignore_spec.match_file(path_str):
|
|
54
|
-
return False
|
|
55
|
-
|
|
56
|
-
return path.suffix.lower() in EXTENSION_TO_LANGUAGE
|
|
57
|
+
return sync_service.should_index(path, ignore_spec)
|
|
57
58
|
|
|
58
59
|
|
|
59
60
|
async def sync_cmd(args):
|
|
60
|
-
# Validate input path
|
|
61
61
|
if args.path:
|
|
62
62
|
args.path = str(validate_path(args.path))
|
|
63
63
|
|
|
@@ -67,39 +67,13 @@ async def sync_cmd(args):
|
|
|
67
67
|
allow_build_execution=getattr(args, "allow_build_execution", False),
|
|
68
68
|
)
|
|
69
69
|
try:
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if args.path:
|
|
79
|
-
target_path = Path(args.path)
|
|
80
|
-
if target_path.is_file():
|
|
81
|
-
if should_index(target_path, ignore_spec):
|
|
82
|
-
await manager.sync_file(str(target_path), force_distill=args.force)
|
|
83
|
-
else:
|
|
84
|
-
paths = [
|
|
85
|
-
str(p)
|
|
86
|
-
for p in target_path.rglob("*")
|
|
87
|
-
if p.is_file() and should_index(p, ignore_spec)
|
|
88
|
-
]
|
|
89
|
-
|
|
90
|
-
if args.verbose:
|
|
91
|
-
logger.info("Indexing %d files...", len(paths))
|
|
92
|
-
await manager.sync_project(paths, force_distill=args.force)
|
|
93
|
-
elif args.all:
|
|
94
|
-
paths = [
|
|
95
|
-
str(p)
|
|
96
|
-
for p in Path(".").rglob("*")
|
|
97
|
-
if p.is_file() and should_index(p, ignore_spec)
|
|
98
|
-
]
|
|
99
|
-
|
|
100
|
-
if args.verbose:
|
|
101
|
-
logger.info("Indexing %d files...", len(paths))
|
|
102
|
-
await manager.sync_project(paths, force_distill=args.force)
|
|
70
|
+
await sync_service.run_sync(
|
|
71
|
+
manager,
|
|
72
|
+
root=Path.cwd(),
|
|
73
|
+
path=args.path,
|
|
74
|
+
index_all=bool(args.all),
|
|
75
|
+
force=bool(args.force),
|
|
76
|
+
)
|
|
103
77
|
|
|
104
78
|
if args.json:
|
|
105
79
|
print(json.dumps({"status": "success", "indexed_files": "auto"}))
|
|
@@ -116,7 +90,7 @@ async def sync_cmd(args):
|
|
|
116
90
|
async def search_cmd(args):
|
|
117
91
|
manager = get_manager(args.db, args.onnx)
|
|
118
92
|
try:
|
|
119
|
-
results = await manager
|
|
93
|
+
results = await run_search(manager, args.query, limit=args.limit)
|
|
120
94
|
|
|
121
95
|
if args.json:
|
|
122
96
|
output = []
|
|
@@ -150,14 +124,12 @@ async def search_cmd(args):
|
|
|
150
124
|
async def api_cmd(args):
|
|
151
125
|
manager = get_manager(args.db, args.onnx)
|
|
152
126
|
try:
|
|
153
|
-
|
|
154
|
-
lang = args.lang or "python"
|
|
155
|
-
api_report = await manager.discovery.extract_api(args.library, language=lang)
|
|
127
|
+
report = await run_api(manager, args.library, lang=args.lang)
|
|
156
128
|
|
|
157
129
|
if args.json:
|
|
158
|
-
print(json.dumps({"library":
|
|
130
|
+
print(json.dumps({"library": report.library, "report": report.report}))
|
|
159
131
|
else:
|
|
160
|
-
print(
|
|
132
|
+
print(report.report)
|
|
161
133
|
except Exception as e:
|
|
162
134
|
logger.error("API discovery failed: %s", e)
|
|
163
135
|
if args.json:
|
|
@@ -200,40 +172,33 @@ def config_cmd(args):
|
|
|
200
172
|
async def setup_cmd(args):
|
|
201
173
|
"""Downloads necessary local models."""
|
|
202
174
|
global_dir = get_global_dir()
|
|
203
|
-
model_dir = global_dir / "models" / "mini-lm"
|
|
204
|
-
model_dir.mkdir(parents=True, exist_ok=True)
|
|
205
|
-
|
|
206
|
-
files = {
|
|
207
|
-
"model.onnx": "https://huggingface.co/naranor/all-MiniLM-L6-v2-onnx/resolve/main/model.onnx",
|
|
208
|
-
"tokenizer.json": "https://huggingface.co/naranor/all-MiniLM-L6-v2-onnx/resolve/main/tokenizer.json",
|
|
209
|
-
}
|
|
210
175
|
|
|
211
176
|
if not args.json:
|
|
212
177
|
print(f"Setting up agent-coderag in {global_dir}...")
|
|
213
178
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
if target_path.exists() and not args.force:
|
|
217
|
-
if not args.json:
|
|
218
|
-
print(f" {name} already exists. Skipping.")
|
|
219
|
-
continue
|
|
220
|
-
|
|
221
|
-
if not args.json:
|
|
222
|
-
print(f" Downloading {name}...")
|
|
223
|
-
tmp_path = target_path.with_suffix(".tmp")
|
|
224
|
-
try:
|
|
225
|
-
response = requests.get(url, stream=True, timeout=30)
|
|
226
|
-
response.raise_for_status()
|
|
227
|
-
with open(tmp_path, "wb") as f:
|
|
228
|
-
for chunk in response.iter_content(chunk_size=8192):
|
|
229
|
-
f.write(chunk)
|
|
230
|
-
os.replace(tmp_path, target_path)
|
|
231
|
-
except Exception as e:
|
|
232
|
-
if tmp_path.exists():
|
|
233
|
-
tmp_path.unlink()
|
|
234
|
-
if not args.json:
|
|
235
|
-
print(f" Error downloading {name}: {e}")
|
|
179
|
+
def _on_progress(event: str, name: str, error: Optional[BaseException]) -> None:
|
|
180
|
+
if args.json:
|
|
236
181
|
return
|
|
182
|
+
if event == "skipped":
|
|
183
|
+
print(f" {name} already exists. Skipping.")
|
|
184
|
+
elif event == "downloading":
|
|
185
|
+
print(f" Downloading {name}...")
|
|
186
|
+
elif event == "error":
|
|
187
|
+
print(f" Error downloading {name}: {error}")
|
|
188
|
+
|
|
189
|
+
try:
|
|
190
|
+
# Pass module-level requests.get so @patch("code_rag.entry.cli.requests.get")
|
|
191
|
+
# continues to intercept downloads in legacy CLI tests.
|
|
192
|
+
await run_setup(
|
|
193
|
+
force=args.force,
|
|
194
|
+
global_dir=global_dir,
|
|
195
|
+
on_progress=_on_progress,
|
|
196
|
+
http_get=requests.get,
|
|
197
|
+
)
|
|
198
|
+
except Exception:
|
|
199
|
+
# Legacy behaviour: per-file error already reported via on_progress;
|
|
200
|
+
# stop here without crashing or printing "Setup complete.".
|
|
201
|
+
return
|
|
237
202
|
|
|
238
203
|
if not args.json:
|
|
239
204
|
print("Setup complete.")
|
|
@@ -253,15 +218,16 @@ def get_manager(
|
|
|
253
218
|
onnx_path: Optional[str] = None,
|
|
254
219
|
allow_build_execution: bool = False,
|
|
255
220
|
) -> CodeRAGManager:
|
|
256
|
-
|
|
221
|
+
"""Wires a manager from the module-level components (legacy patch points).
|
|
222
|
+
|
|
223
|
+
Mirrors ``code_rag.services.factory.create_manager`` but resolves the
|
|
224
|
+
components through this module so existing CLI tests can substitute them.
|
|
225
|
+
"""
|
|
257
226
|
config = DistillerConfig.load()
|
|
258
227
|
distiller = Distiller(config)
|
|
259
228
|
embedder = Embedder(model_path=onnx_path)
|
|
260
229
|
|
|
261
|
-
# 2. Setup Storage
|
|
262
230
|
storage = DuckDBStorage(db_path, embedder=embedder)
|
|
263
|
-
|
|
264
|
-
# 3. Setup Parser
|
|
265
231
|
parser = MultiParser()
|
|
266
232
|
|
|
267
233
|
return CodeRAGManager(
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from code_rag.intelligence.distiller import DistillerConfig
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def load_or_update_config(
|
|
5
|
+
url: str | None = None,
|
|
6
|
+
key: str | None = None,
|
|
7
|
+
model: str | None = None,
|
|
8
|
+
provider: str | None = None,
|
|
9
|
+
) -> DistillerConfig:
|
|
10
|
+
"""Loads the persisted config, optionally updating and saving it."""
|
|
11
|
+
config = DistillerConfig.load()
|
|
12
|
+
if not any([url, key, model, provider]):
|
|
13
|
+
return config
|
|
14
|
+
|
|
15
|
+
if url is not None:
|
|
16
|
+
config.api_base = url
|
|
17
|
+
if key is not None:
|
|
18
|
+
config.api_key = key
|
|
19
|
+
if model is not None:
|
|
20
|
+
config.model = model
|
|
21
|
+
if provider is not None:
|
|
22
|
+
config.provider = provider
|
|
23
|
+
|
|
24
|
+
config.save()
|
|
25
|
+
return config
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
from code_rag.api.models import ApiReport
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
async def run_api(manager, library: str, *, lang: str | None = None) -> ApiReport:
|
|
5
|
+
"""Discovers and returns the public API report for a library."""
|
|
6
|
+
language = lang or "python"
|
|
7
|
+
report = await manager.discovery.extract_api(library, language=language)
|
|
8
|
+
return ApiReport(library=library, language=language, report=report)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from code_rag.core.manager import CodeRAGManager
|
|
4
|
+
from code_rag.intelligence.distiller import Distiller, DistillerConfig
|
|
5
|
+
from code_rag.intelligence.embedder import Embedder
|
|
6
|
+
from code_rag.parsers.multi_parser import MultiParser
|
|
7
|
+
from code_rag.storage.duckdb_impl import DuckDBStorage
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def create_manager(
|
|
11
|
+
db_path: str,
|
|
12
|
+
onnx_path: Optional[str] = None,
|
|
13
|
+
allow_build_execution: bool = False,
|
|
14
|
+
) -> CodeRAGManager:
|
|
15
|
+
"""Builds a fully wired CodeRAGManager (storage, parser, distiller)."""
|
|
16
|
+
config = DistillerConfig.load()
|
|
17
|
+
distiller = Distiller(config)
|
|
18
|
+
embedder = Embedder(model_path=onnx_path)
|
|
19
|
+
|
|
20
|
+
storage = DuckDBStorage(db_path, embedder=embedder)
|
|
21
|
+
parser = MultiParser()
|
|
22
|
+
|
|
23
|
+
return CodeRAGManager(
|
|
24
|
+
storage,
|
|
25
|
+
parser,
|
|
26
|
+
distiller,
|
|
27
|
+
allow_build_execution=allow_build_execution,
|
|
28
|
+
)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Any, Callable, Optional
|
|
4
|
+
|
|
5
|
+
import requests
|
|
6
|
+
|
|
7
|
+
from code_rag.api.models import SetupResult
|
|
8
|
+
from code_rag.intelligence.embedder import get_global_dir
|
|
9
|
+
|
|
10
|
+
MODEL_FILES = {
|
|
11
|
+
"model.onnx": "https://huggingface.co/naranor/all-MiniLM-L6-v2-onnx/resolve/main/model.onnx",
|
|
12
|
+
"tokenizer.json": "https://huggingface.co/naranor/all-MiniLM-L6-v2-onnx/resolve/main/tokenizer.json",
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
HttpGet = Callable[..., Any]
|
|
16
|
+
|
|
17
|
+
# Progress callback signature: (event, name, error) where event is one of
|
|
18
|
+
# "downloading", "skipped", "error" and error is set only for "error".
|
|
19
|
+
ProgressCallback = Callable[[str, str, Optional[BaseException]], None]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _download_file(get: HttpGet, url: str, target_path: Path) -> None:
|
|
23
|
+
tmp_path = target_path.with_suffix(".tmp")
|
|
24
|
+
try:
|
|
25
|
+
response = get(url, stream=True, timeout=30)
|
|
26
|
+
response.raise_for_status()
|
|
27
|
+
with open(tmp_path, "wb") as handle:
|
|
28
|
+
for chunk in response.iter_content(chunk_size=8192):
|
|
29
|
+
handle.write(chunk)
|
|
30
|
+
os.replace(tmp_path, target_path)
|
|
31
|
+
except Exception:
|
|
32
|
+
if tmp_path.exists():
|
|
33
|
+
tmp_path.unlink()
|
|
34
|
+
raise
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
async def run_setup(
|
|
38
|
+
*,
|
|
39
|
+
force: bool = False,
|
|
40
|
+
global_dir: Optional[Path] = None,
|
|
41
|
+
on_progress: Optional[ProgressCallback] = None,
|
|
42
|
+
http_get: Optional[HttpGet] = None,
|
|
43
|
+
) -> SetupResult:
|
|
44
|
+
"""Downloads missing local embedding model files into the global dir."""
|
|
45
|
+
target_dir = global_dir if global_dir is not None else get_global_dir()
|
|
46
|
+
model_dir = target_dir / "models" / "mini-lm"
|
|
47
|
+
model_dir.mkdir(parents=True, exist_ok=True)
|
|
48
|
+
get: HttpGet = http_get or requests.get
|
|
49
|
+
|
|
50
|
+
downloaded: list[str] = []
|
|
51
|
+
skipped: list[str] = []
|
|
52
|
+
|
|
53
|
+
for name, url in MODEL_FILES.items():
|
|
54
|
+
target_path = model_dir / name
|
|
55
|
+
if target_path.exists() and not force:
|
|
56
|
+
skipped.append(name)
|
|
57
|
+
if on_progress:
|
|
58
|
+
on_progress("skipped", name, None)
|
|
59
|
+
continue
|
|
60
|
+
|
|
61
|
+
if on_progress:
|
|
62
|
+
on_progress("downloading", name, None)
|
|
63
|
+
|
|
64
|
+
try:
|
|
65
|
+
_download_file(get, url, target_path)
|
|
66
|
+
downloaded.append(name)
|
|
67
|
+
except Exception as exc:
|
|
68
|
+
if on_progress:
|
|
69
|
+
on_progress("error", name, exc)
|
|
70
|
+
raise
|
|
71
|
+
|
|
72
|
+
return SetupResult(model_dir=str(model_dir), downloaded=downloaded, skipped=skipped)
|