java-codebase-rag 0.9.4__py3-none-any.whl → 0.9.6__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.
- java_codebase_rag/absence/__init__.py +0 -0
- java_codebase_rag/absence/absence_diagnosis.py +700 -0
- java_codebase_rag/absence/absence_types.py +124 -0
- java_codebase_rag/absence/absence_vocab.py +455 -0
- java_codebase_rag/analysis/__init__.py +0 -0
- pr_analysis.py → java_codebase_rag/analysis/pr_analysis.py +1 -1
- resolve_service.py → java_codebase_rag/analysis/resolve_service.py +73 -6
- java_codebase_rag/ast/__init__.py +0 -0
- ast_java.py → java_codebase_rag/ast/ast_java.py +5 -5
- java_codebase_rag/cli.py +13 -18
- java_codebase_rag/config.py +116 -0
- java_codebase_rag/graph/__init__.py +0 -0
- build_ast_graph.py → java_codebase_rag/graph/build_ast_graph.py +89 -11
- graph_enrich.py → java_codebase_rag/graph/graph_enrich.py +248 -3
- graph_types.py → java_codebase_rag/graph/graph_types.py +6 -2
- java_ontology.py → java_codebase_rag/graph/java_ontology.py +1 -1
- ladybug_queries.py → java_codebase_rag/graph/ladybug_queries.py +6 -6
- java_codebase_rag/index/__init__.py +0 -0
- java_index_flow_lancedb.py → java_codebase_rag/index/java_index_flow_lancedb.py +30 -10
- java_codebase_rag/install_data/__init__.py +0 -0
- java_codebase_rag/jrag.py +71 -16
- java_codebase_rag/jrag_envelope.py +13 -4
- java_codebase_rag/jrag_hints.py +1 -1
- java_codebase_rag/jrag_render.py +67 -3
- java_codebase_rag/mcp/__init__.py +0 -0
- mcp_hints.py → java_codebase_rag/mcp/mcp_hints.py +1 -1
- mcp_v2.py → java_codebase_rag/mcp/mcp_v2.py +280 -81
- server.py → java_codebase_rag/mcp/server.py +138 -54
- java_codebase_rag/pipeline.py +26 -7
- java_codebase_rag/search/__init__.py +0 -0
- search_lancedb.py → java_codebase_rag/search/search_lancedb.py +53 -314
- java_codebase_rag/search/search_lexical.py +329 -0
- java_codebase_rag/search/search_scoring.py +338 -0
- {java_codebase_rag-0.9.4.dist-info → java_codebase_rag-0.9.6.dist-info}/METADATA +2 -2
- java_codebase_rag-0.9.6.dist-info/RECORD +57 -0
- {java_codebase_rag-0.9.4.dist-info → java_codebase_rag-0.9.6.dist-info}/entry_points.txt +1 -1
- java_codebase_rag-0.9.6.dist-info/top_level.txt +1 -0
- java_codebase_rag-0.9.4.dist-info/RECORD +0 -44
- java_codebase_rag-0.9.4.dist-info/top_level.txt +0 -19
- /brownfield_events.py → /java_codebase_rag/ast/brownfield_events.py +0 -0
- /chunk_heuristics.py → /java_codebase_rag/ast/chunk_heuristics.py +0 -0
- /path_filtering.py → /java_codebase_rag/graph/path_filtering.py +0 -0
- /java_index_v1_common.py → /java_codebase_rag/index/java_index_v1_common.py +0 -0
- /index_common.py → /java_codebase_rag/search/index_common.py +0 -0
- {java_codebase_rag-0.9.4.dist-info → java_codebase_rag-0.9.6.dist-info}/WHEEL +0 -0
- {java_codebase_rag-0.9.4.dist-info → java_codebase_rag-0.9.6.dist-info}/licenses/LICENSE +0 -0
|
@@ -9,11 +9,17 @@ import time
|
|
|
9
9
|
from pathlib import Path
|
|
10
10
|
from typing import Literal
|
|
11
11
|
|
|
12
|
-
import mcp_v2
|
|
13
|
-
from
|
|
12
|
+
from java_codebase_rag.mcp import mcp_v2
|
|
13
|
+
from java_codebase_rag.analysis import resolve_service
|
|
14
|
+
from java_codebase_rag.search.index_common import SBERT_MODEL
|
|
14
15
|
from java_codebase_rag.cli_progress import (
|
|
15
16
|
accumulate_and_relay_subprocess_streams,
|
|
16
17
|
)
|
|
18
|
+
from java_codebase_rag.pipeline import (
|
|
19
|
+
VECTORS_SKIPPED_GRAPH_ONLY,
|
|
20
|
+
cocoindex_bin as resolve_cocoindex_bin,
|
|
21
|
+
vector_stack_installed,
|
|
22
|
+
)
|
|
17
23
|
from java_codebase_rag.progress import ProgressEvent
|
|
18
24
|
from java_codebase_rag._fdlimit import raise_fd_limit
|
|
19
25
|
from java_codebase_rag.config import (
|
|
@@ -23,13 +29,20 @@ from java_codebase_rag.config import (
|
|
|
23
29
|
resolved_sbert_model_for_process_env,
|
|
24
30
|
resolve_operator_config,
|
|
25
31
|
)
|
|
26
|
-
from ladybug_queries import LadybugGraph, resolve_ladybug_path
|
|
32
|
+
from java_codebase_rag.graph.ladybug_queries import LadybugGraph, resolve_ladybug_path
|
|
27
33
|
from mcp.server.fastmcp import FastMCP
|
|
28
34
|
from pydantic import BaseModel, Field
|
|
29
35
|
# NOTE: search_lancedb.TABLES is imported lazily in list_code_index_tables_payload() — it
|
|
30
36
|
# pulls lancedb/torch and is unavailable on graph-only installs (macOS Intel).
|
|
31
37
|
|
|
32
38
|
_COCOINDEX_TARGET = "java_index_flow_lancedb.py:JavaCodeIndexLance"
|
|
39
|
+
|
|
40
|
+
# Package-internal locations of the cocoindex flow and the graph builder, both
|
|
41
|
+
# executed by file path (see java_codebase_rag.pipeline). Derived from this
|
|
42
|
+
# file's location so they resolve under editable and wheel installs alike.
|
|
43
|
+
_PKG_DIR = Path(__file__).resolve().parent.parent
|
|
44
|
+
_FLOW_FILE = _PKG_DIR / "index" / "java_index_flow_lancedb.py"
|
|
45
|
+
_BUILDER_FILE = _PKG_DIR / "graph" / "build_ast_graph.py"
|
|
33
46
|
_INSTRUCTIONS = (
|
|
34
47
|
"Java codebase graph navigator over an indexed Java codebase. "
|
|
35
48
|
"Tools: search (NL/code locate), find (structured NodeFilter), describe (one node + edge_summary: stored edge-label counts and optional composed keys for type Symbols and override-axis virtual keys for method Symbols), "
|
|
@@ -112,7 +125,7 @@ class ScopeManager:
|
|
|
112
125
|
self._log_detection()
|
|
113
126
|
|
|
114
127
|
def _detect_scope(self) -> str | None:
|
|
115
|
-
from graph_enrich import detect_microservice_from_path
|
|
128
|
+
from java_codebase_rag.graph.graph_enrich import detect_microservice_from_path
|
|
116
129
|
|
|
117
130
|
candidate = detect_microservice_from_path(Path.cwd(), self.source_root)
|
|
118
131
|
if candidate is None:
|
|
@@ -298,7 +311,7 @@ def _graph_meta_output() -> GraphMetaOutput:
|
|
|
298
311
|
|
|
299
312
|
def list_code_index_tables_payload() -> IndexInfoOutput:
|
|
300
313
|
try:
|
|
301
|
-
from search_lancedb import TABLES
|
|
314
|
+
from java_codebase_rag.search.search_lancedb import TABLES
|
|
302
315
|
|
|
303
316
|
tables = dict(TABLES)
|
|
304
317
|
except ImportError:
|
|
@@ -314,6 +327,61 @@ def list_code_index_tables_payload() -> IndexInfoOutput:
|
|
|
314
327
|
)
|
|
315
328
|
|
|
316
329
|
|
|
330
|
+
async def _run_graph_phase(
|
|
331
|
+
root: Path,
|
|
332
|
+
*,
|
|
333
|
+
quiet: bool,
|
|
334
|
+
verbose: bool,
|
|
335
|
+
on_progress: object | None,
|
|
336
|
+
on_progress_console: object | None,
|
|
337
|
+
) -> tuple[int | None, str, str, bool]:
|
|
338
|
+
"""Run ``build_ast_graph.py`` and return ``(code, stdout, stderr, started)``.
|
|
339
|
+
|
|
340
|
+
Shared by the vectors→graph refresh path and the graph-only path (macOS Intel,
|
|
341
|
+
where the vector stack is gated off). ``started`` is True only when the graph
|
|
342
|
+
subprocess was actually created, so callers set ``phases_run`` accurately — the
|
|
343
|
+
CLI maps an empty ``phases_run`` to a preflight exit code 2 (nothing spawned).
|
|
344
|
+
A missing builder or a spawn failure returns ``started=False`` with the graph
|
|
345
|
+
code carrying the reason (``None`` for missing builder, ``-1`` for spawn error).
|
|
346
|
+
"""
|
|
347
|
+
builder = _BUILDER_FILE
|
|
348
|
+
if not builder.is_file():
|
|
349
|
+
return None, "", "", False
|
|
350
|
+
try:
|
|
351
|
+
graph_args = [
|
|
352
|
+
sys.executable,
|
|
353
|
+
str(builder),
|
|
354
|
+
"--source-root",
|
|
355
|
+
str(root),
|
|
356
|
+
"--ladybug-path",
|
|
357
|
+
resolve_ladybug_path(),
|
|
358
|
+
]
|
|
359
|
+
if not quiet:
|
|
360
|
+
graph_args.append("--verbose")
|
|
361
|
+
gproc = await asyncio.create_subprocess_exec(
|
|
362
|
+
*graph_args,
|
|
363
|
+
cwd=str(root),
|
|
364
|
+
env=_cocoindex_subprocess_env(root),
|
|
365
|
+
stdout=asyncio.subprocess.PIPE,
|
|
366
|
+
stderr=asyncio.subprocess.PIPE,
|
|
367
|
+
)
|
|
368
|
+
if quiet:
|
|
369
|
+
gout_b, gerr_b = await gproc.communicate()
|
|
370
|
+
else:
|
|
371
|
+
gout_b, gerr_b = await accumulate_and_relay_subprocess_streams(
|
|
372
|
+
gproc, relay=True, verbose=verbose,
|
|
373
|
+
on_progress=on_progress, on_progress_console=on_progress_console,
|
|
374
|
+
)
|
|
375
|
+
return (
|
|
376
|
+
gproc.returncode,
|
|
377
|
+
gout_b.decode(errors="replace"),
|
|
378
|
+
gerr_b.decode(errors="replace"),
|
|
379
|
+
True,
|
|
380
|
+
)
|
|
381
|
+
except Exception as exc:
|
|
382
|
+
return -1, "", f"graph builder spawn failed: {exc}", False
|
|
383
|
+
|
|
384
|
+
|
|
317
385
|
async def run_refresh_pipeline(
|
|
318
386
|
*,
|
|
319
387
|
quiet: bool = False,
|
|
@@ -322,7 +390,51 @@ async def run_refresh_pipeline(
|
|
|
322
390
|
on_progress_console: object | None = None,
|
|
323
391
|
) -> RefreshIndexOutput:
|
|
324
392
|
root = _project_root()
|
|
325
|
-
|
|
393
|
+
if not vector_stack_installed():
|
|
394
|
+
# Graph-only install (macOS Intel): the vector stack (cocoindex/lancedb/
|
|
395
|
+
# sentence-transformers) is gated off by PEP 508 markers and uninstallable,
|
|
396
|
+
# so the cocoindex binary is absent. Skip the vectors phase and build the
|
|
397
|
+
# graph only — mirroring init/increment, which treat cocoindex-absent as a
|
|
398
|
+
# skip, not a failure (the graph layer is the supported surface there). No
|
|
399
|
+
# vectors progress event is emitted, so the renderer's vectors task stays
|
|
400
|
+
# invisible (its "never spawned" invariant) instead of hanging at running.
|
|
401
|
+
print(VECTORS_SKIPPED_GRAPH_ONLY, file=sys.stderr, flush=True)
|
|
402
|
+
if not quiet:
|
|
403
|
+
print(file=sys.stderr, flush=True)
|
|
404
|
+
graph_code, graph_out, graph_err, started = await _run_graph_phase(
|
|
405
|
+
root, quiet=quiet, verbose=verbose,
|
|
406
|
+
on_progress=on_progress, on_progress_console=on_progress_console,
|
|
407
|
+
)
|
|
408
|
+
ok = graph_code == 0
|
|
409
|
+
if not ok:
|
|
410
|
+
message = (
|
|
411
|
+
f"graph builder exit {graph_code}"
|
|
412
|
+
if graph_code is not None
|
|
413
|
+
else (graph_err.strip() or "graph builder unavailable")
|
|
414
|
+
)
|
|
415
|
+
else:
|
|
416
|
+
message = "reprocess completed (graph-only; vectors skipped — vector stack not installed)"
|
|
417
|
+
return RefreshIndexOutput(
|
|
418
|
+
success=ok,
|
|
419
|
+
exit_code=None,
|
|
420
|
+
stdout="",
|
|
421
|
+
stderr="",
|
|
422
|
+
message=message,
|
|
423
|
+
graph_exit_code=graph_code,
|
|
424
|
+
graph_stdout=graph_out[-4000:] if len(graph_out) > 4000 else graph_out,
|
|
425
|
+
graph_stderr=graph_err[-4000:] if len(graph_err) > 4000 else graph_err,
|
|
426
|
+
phases_run=["graph"] if started else [],
|
|
427
|
+
optimize_error=None,
|
|
428
|
+
)
|
|
429
|
+
# Resolve cocoindex the same way the sync path does (pipeline.cocoindex_bin):
|
|
430
|
+
# next to the interpreter first, then via PATH (``shutil.which``). A console
|
|
431
|
+
# script legitimately lives away from the venv python — e.g. ``pip install
|
|
432
|
+
# --user`` places it in ``~/.local/bin``. Honoring PATH keeps ``reprocess``
|
|
433
|
+
# (no flags) consistent with init/increment/``reprocess --vectors-only``,
|
|
434
|
+
# which all resolve through cocoindex_bin(); previously this path checked
|
|
435
|
+
# only next-to-python and failed with "cocoindex not found next to Python"
|
|
436
|
+
# even though cocoindex was reachable on PATH.
|
|
437
|
+
cocoindex_bin = resolve_cocoindex_bin()
|
|
326
438
|
if not cocoindex_bin.is_file():
|
|
327
439
|
# 127 pre-spawn: emit a terminal failed vectors event so the renderer's
|
|
328
440
|
# task doesn't hang at running (matches the sync pipeline path).
|
|
@@ -330,23 +442,21 @@ async def run_refresh_pipeline(
|
|
|
330
442
|
on_progress(ProgressEvent(kind="vectors", phase=None, pass_=None, done=None, total=None, status="failed", elapsed_s=None))
|
|
331
443
|
return RefreshIndexOutput(
|
|
332
444
|
success=False,
|
|
333
|
-
message=
|
|
445
|
+
message=(
|
|
446
|
+
f"cocoindex not found next to Python ({cocoindex_bin}) or on PATH; "
|
|
447
|
+
"install cocoindex[lancedb] into the same venv or add its bin/ to PATH."
|
|
448
|
+
),
|
|
334
449
|
phases_run=[],
|
|
335
450
|
)
|
|
336
|
-
flow_path =
|
|
337
|
-
bundle_dir = Path(__file__).resolve().parent
|
|
451
|
+
flow_path = _FLOW_FILE
|
|
338
452
|
if not flow_path.is_file():
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
success=False,
|
|
347
|
-
message=f"java_index_flow_lancedb.py not found under {root} nor {bundle_dir}",
|
|
348
|
-
phases_run=[],
|
|
349
|
-
)
|
|
453
|
+
if on_progress is not None:
|
|
454
|
+
on_progress(ProgressEvent(kind="vectors", phase=None, pass_=None, done=None, total=None, status="failed", elapsed_s=None))
|
|
455
|
+
return RefreshIndexOutput(
|
|
456
|
+
success=False,
|
|
457
|
+
message=f"java_index_flow_lancedb.py not found at {flow_path}",
|
|
458
|
+
phases_run=[],
|
|
459
|
+
)
|
|
350
460
|
proc: asyncio.subprocess.Process | None = None
|
|
351
461
|
out_b, err_b = b"", b""
|
|
352
462
|
# DROP the Lance target tables so the update takes the fast INSERT path
|
|
@@ -466,40 +576,12 @@ async def run_refresh_pipeline(
|
|
|
466
576
|
except Exception as exc:
|
|
467
577
|
optimize_error = f"lance optimize failed: {exc}"
|
|
468
578
|
print(f"java-codebase-rag: {optimize_error}", file=sys.stderr)
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
"--source-root",
|
|
476
|
-
str(root),
|
|
477
|
-
"--ladybug-path",
|
|
478
|
-
resolve_ladybug_path(),
|
|
479
|
-
]
|
|
480
|
-
if not quiet:
|
|
481
|
-
graph_args.append("--verbose")
|
|
482
|
-
gproc = await asyncio.create_subprocess_exec(
|
|
483
|
-
*graph_args,
|
|
484
|
-
cwd=str(root),
|
|
485
|
-
env=_cocoindex_subprocess_env(root),
|
|
486
|
-
stdout=asyncio.subprocess.PIPE,
|
|
487
|
-
stderr=asyncio.subprocess.PIPE,
|
|
488
|
-
)
|
|
489
|
-
phases_run = ["vectors", "graph"]
|
|
490
|
-
if quiet:
|
|
491
|
-
gout_b, gerr_b = await gproc.communicate()
|
|
492
|
-
else:
|
|
493
|
-
gout_b, gerr_b = await accumulate_and_relay_subprocess_streams(
|
|
494
|
-
gproc, relay=True, verbose=verbose,
|
|
495
|
-
on_progress=on_progress, on_progress_console=on_progress_console,
|
|
496
|
-
)
|
|
497
|
-
graph_code = gproc.returncode
|
|
498
|
-
graph_out = gout_b.decode(errors="replace")
|
|
499
|
-
graph_err = gerr_b.decode(errors="replace")
|
|
500
|
-
except Exception as exc:
|
|
501
|
-
graph_code = -1
|
|
502
|
-
graph_err = f"graph builder spawn failed: {exc}"
|
|
579
|
+
graph_code, graph_out, graph_err, graph_started = await _run_graph_phase(
|
|
580
|
+
root, quiet=quiet, verbose=verbose,
|
|
581
|
+
on_progress=on_progress, on_progress_console=on_progress_console,
|
|
582
|
+
)
|
|
583
|
+
if graph_started:
|
|
584
|
+
phases_run = ["vectors", "graph"]
|
|
503
585
|
message: str | None = None
|
|
504
586
|
if not ok:
|
|
505
587
|
message = f"cocoindex exit {proc.returncode}"
|
|
@@ -788,6 +870,8 @@ def main() -> None:
|
|
|
788
870
|
cfg = resolve_operator_config(source_root=_source_root_for_operator_config())
|
|
789
871
|
cfg.apply_to_os_environ()
|
|
790
872
|
mcp_v2.set_hints_enabled(cfg.hints_enabled)
|
|
873
|
+
mcp_v2.set_absence_config(cfg)
|
|
874
|
+
resolve_service.set_absence_config(cfg)
|
|
791
875
|
|
|
792
876
|
# Initialize scope manager for automatic microservice detection
|
|
793
877
|
global _scope_manager
|
java_codebase_rag/pipeline.py
CHANGED
|
@@ -18,9 +18,28 @@ from java_codebase_rag.progress import ProgressEvent, ProgressRelay, make_relay
|
|
|
18
18
|
|
|
19
19
|
COCOINDEX_TARGET = "java_index_flow_lancedb.py:JavaCodeIndexLance"
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
# Operator-facing line printed when an indexing command skips the vectors phase
|
|
22
|
+
# because the vector stack is absent (graph-only install, e.g. macOS Intel where
|
|
23
|
+
# cocoindex[lancedb]/lancedb/sentence-transformers are gated off by PEP 508 markers).
|
|
24
|
+
# Single source of truth — imported by cli.py (init/increment) and server.py
|
|
25
|
+
# (reprocess) so the wording can't drift between the two paths.
|
|
26
|
+
VECTORS_SKIPPED_GRAPH_ONLY = (
|
|
27
|
+
"java-codebase-rag: vectors skipped — vector stack not installed on this platform "
|
|
28
|
+
"(graph-only mode). The graph is built/refreshed; semantic search is unavailable."
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# Package-internal locations of the cocoindex flow definition and the graph
|
|
33
|
+
# builder. Both are executed *by file path* — cocoindex loads the flow via a
|
|
34
|
+
# ``file:Class`` target (COCOINDEX_TARGET below) and the builder runs as
|
|
35
|
+
# ``python <path>`` — so we must resolve their paths without importing them
|
|
36
|
+
# (this module deliberately stays free of heavy ML imports at import time).
|
|
37
|
+
# Derived from this file's location so they resolve under both editable and
|
|
38
|
+
# wheel installs.
|
|
39
|
+
_PKG_DIR = Path(__file__).resolve().parent
|
|
40
|
+
_FLOW_DIR = _PKG_DIR / "index"
|
|
41
|
+
_FLOW_FILE = _FLOW_DIR / "java_index_flow_lancedb.py"
|
|
42
|
+
_BUILDER_FILE = _PKG_DIR / "graph" / "build_ast_graph.py"
|
|
24
43
|
|
|
25
44
|
|
|
26
45
|
def cocoindex_bin() -> Path:
|
|
@@ -256,7 +275,7 @@ def _run_cocoindex_update_impl(
|
|
|
256
275
|
stdout="",
|
|
257
276
|
stderr=f"cocoindex not found: {exe}",
|
|
258
277
|
)
|
|
259
|
-
bd =
|
|
278
|
+
bd = _FLOW_DIR
|
|
260
279
|
flow = bd / "java_index_flow_lancedb.py"
|
|
261
280
|
if not flow.is_file():
|
|
262
281
|
if on_progress is not None:
|
|
@@ -333,7 +352,7 @@ def run_cocoindex_drop(env: dict[str, str], *, quiet: bool) -> subprocess.Comple
|
|
|
333
352
|
stdout="",
|
|
334
353
|
stderr=f"cocoindex not found: {exe}",
|
|
335
354
|
)
|
|
336
|
-
bd =
|
|
355
|
+
bd = _FLOW_DIR
|
|
337
356
|
cmd = [str(exe), "drop", COCOINDEX_TARGET, "-f"]
|
|
338
357
|
if quiet:
|
|
339
358
|
cmd.append("-q")
|
|
@@ -356,7 +375,7 @@ def run_build_ast_graph(
|
|
|
356
375
|
on_progress: Callable[[ProgressEvent], None] | None = None,
|
|
357
376
|
on_progress_console: object | None = None,
|
|
358
377
|
) -> subprocess.CompletedProcess[str]:
|
|
359
|
-
builder =
|
|
378
|
+
builder = _BUILDER_FILE
|
|
360
379
|
if not builder.is_file():
|
|
361
380
|
return subprocess.CompletedProcess(
|
|
362
381
|
args=[],
|
|
@@ -414,7 +433,7 @@ def run_incremental_graph(
|
|
|
414
433
|
on_progress_console: object | None = None,
|
|
415
434
|
) -> subprocess.CompletedProcess[str]:
|
|
416
435
|
"""Run incremental graph rebuild by passing --incremental flag to build_ast_graph.py."""
|
|
417
|
-
builder =
|
|
436
|
+
builder = _BUILDER_FILE
|
|
418
437
|
if not builder.is_file():
|
|
419
438
|
return subprocess.CompletedProcess(
|
|
420
439
|
args=[],
|
|
File without changes
|