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
|
@@ -6,11 +6,14 @@ by the CLI layer. Provides resolve_v2(identifier, hint_kind, graph) -> ResolveOu
|
|
|
6
6
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
|
-
from typing import Literal
|
|
9
|
+
from typing import Any, Literal
|
|
10
10
|
|
|
11
11
|
from pydantic import BaseModel, ConfigDict, Field
|
|
12
12
|
|
|
13
|
-
from
|
|
13
|
+
from java_codebase_rag.absence.absence_types import AbsenceDiagnosis
|
|
14
|
+
from java_codebase_rag.absence.absence_diagnosis import diagnose
|
|
15
|
+
from java_codebase_rag.absence.absence_vocab import get_vocabulary_index
|
|
16
|
+
from java_codebase_rag.graph.graph_types import (
|
|
14
17
|
NodeRef,
|
|
15
18
|
StructuredHint,
|
|
16
19
|
_hints_or_skip,
|
|
@@ -18,9 +21,9 @@ from graph_types import (
|
|
|
18
21
|
_to_structured_hints,
|
|
19
22
|
set_hints_enabled,
|
|
20
23
|
)
|
|
21
|
-
from java_ontology import ResolveReason
|
|
22
|
-
from ladybug_queries import LadybugGraph
|
|
23
|
-
from mcp_hints import MCP_HINTS_STRUCTURED_FIELD_DESCRIPTION
|
|
24
|
+
from java_codebase_rag.graph.java_ontology import ResolveReason
|
|
25
|
+
from java_codebase_rag.graph.ladybug_queries import LadybugGraph
|
|
26
|
+
from java_codebase_rag.mcp.mcp_hints import MCP_HINTS_STRUCTURED_FIELD_DESCRIPTION
|
|
24
27
|
|
|
25
28
|
__all__ = [
|
|
26
29
|
"resolve_v2",
|
|
@@ -28,6 +31,7 @@ __all__ = [
|
|
|
28
31
|
"ResolveCandidate",
|
|
29
32
|
"ResolveStatus",
|
|
30
33
|
"set_hints_enabled",
|
|
34
|
+
"set_absence_config",
|
|
31
35
|
]
|
|
32
36
|
|
|
33
37
|
|
|
@@ -35,6 +39,35 @@ ResolveStatus = Literal["one", "many", "none"]
|
|
|
35
39
|
|
|
36
40
|
_RESOLVE_CANDIDATE_CAP = 10
|
|
37
41
|
|
|
42
|
+
# Module-level holder for absence diagnosis config (set by server.py)
|
|
43
|
+
_absence_config: Any = None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def set_absence_config(cfg: Any) -> None:
|
|
47
|
+
"""Set the global absence diagnosis config for resolve service.
|
|
48
|
+
|
|
49
|
+
Mirrors set_hints_enabled: called from server.py to make cfg reachable
|
|
50
|
+
in resolve functions without threading it through every signature.
|
|
51
|
+
"""
|
|
52
|
+
global _absence_config
|
|
53
|
+
_absence_config = cfg
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _get_absence_config() -> Any:
|
|
57
|
+
"""Get the absence diagnosis config, with safe fallback.
|
|
58
|
+
|
|
59
|
+
Returns the module-level config if set; otherwise falls back to a
|
|
60
|
+
default config (for direct tool calls in tests without server init).
|
|
61
|
+
"""
|
|
62
|
+
if _absence_config is not None:
|
|
63
|
+
return _absence_config
|
|
64
|
+
|
|
65
|
+
# Fallback: resolve from cwd for direct test calls
|
|
66
|
+
from java_codebase_rag.config import resolve_operator_config
|
|
67
|
+
from pathlib import Path
|
|
68
|
+
|
|
69
|
+
return resolve_operator_config(source_root=Path.cwd())
|
|
70
|
+
|
|
38
71
|
_RESOLVE_REASON_PRIORITY: dict[ResolveReason, int] = {
|
|
39
72
|
"exact_id": 0,
|
|
40
73
|
"exact_fqn": 1,
|
|
@@ -129,6 +162,7 @@ class ResolveOutput(BaseModel):
|
|
|
129
162
|
resolved_identifier: str | None = None
|
|
130
163
|
advisories: list[str] = Field(default_factory=list, description="Pure informational text with no tool call suggestion")
|
|
131
164
|
hints_structured: list[StructuredHint] = Field(default_factory=list, description=MCP_HINTS_STRUCTURED_FIELD_DESCRIPTION)
|
|
165
|
+
absence: AbsenceDiagnosis | None = None
|
|
132
166
|
|
|
133
167
|
|
|
134
168
|
def _resolve_validate_identifier(raw: str) -> tuple[str | None, str | None]:
|
|
@@ -566,6 +600,9 @@ def _resolve_finalize_success(
|
|
|
566
600
|
trimmed: str,
|
|
567
601
|
hint_kind: Literal["symbol", "route", "client", "producer"] | None,
|
|
568
602
|
matches: list[ResolveCandidate],
|
|
603
|
+
graph: LadybugGraph | None = None,
|
|
604
|
+
microservice: str = "",
|
|
605
|
+
module: str = "",
|
|
569
606
|
) -> ResolveOutput:
|
|
570
607
|
if not matches:
|
|
571
608
|
out = ResolveOutput(
|
|
@@ -591,6 +628,28 @@ def _resolve_finalize_success(
|
|
|
591
628
|
resolved_identifier=trimmed,
|
|
592
629
|
)
|
|
593
630
|
|
|
631
|
+
# Absence diagnosis for empty results (status="none")
|
|
632
|
+
cfg = _get_absence_config()
|
|
633
|
+
diag: AbsenceDiagnosis | None = None
|
|
634
|
+
if not matches:
|
|
635
|
+
g = graph
|
|
636
|
+
vocab = get_vocabulary_index(g, cfg)
|
|
637
|
+
# Map hint_kind to filter_kind (symbol -> "symbol", etc.)
|
|
638
|
+
filter_kind = hint_kind if hint_kind in ("symbol", "route", "client", "producer") else None
|
|
639
|
+
# Build scope from microservice/module params
|
|
640
|
+
scope = {"microservice": microservice or "", "module": module or ""}
|
|
641
|
+
diag = diagnose(
|
|
642
|
+
tool="resolve",
|
|
643
|
+
query=trimmed,
|
|
644
|
+
filt=None,
|
|
645
|
+
filter_kind=filter_kind,
|
|
646
|
+
root_node=None,
|
|
647
|
+
scope=scope,
|
|
648
|
+
vocab=vocab,
|
|
649
|
+
graph=g,
|
|
650
|
+
cfg=cfg,
|
|
651
|
+
)
|
|
652
|
+
|
|
594
653
|
path_prefix_seed, target_service_seed = _resolve_seeds_for_hints(trimmed)
|
|
595
654
|
hint_payload = {
|
|
596
655
|
"status": out.status,
|
|
@@ -604,6 +663,7 @@ def _resolve_finalize_success(
|
|
|
604
663
|
out = out.model_copy(update={
|
|
605
664
|
"advisories": raw_advisories,
|
|
606
665
|
"hints_structured": _to_structured_hints(raw_struct),
|
|
666
|
+
"absence": diag,
|
|
607
667
|
})
|
|
608
668
|
_resolve_assert_invariants(out)
|
|
609
669
|
return out
|
|
@@ -660,7 +720,14 @@ def resolve_v2(
|
|
|
660
720
|
deduped = _resolve_dedupe_candidates(raw)
|
|
661
721
|
ranked = _resolve_rank_candidates(deduped)
|
|
662
722
|
capped = ranked[:_RESOLVE_CANDIDATE_CAP]
|
|
663
|
-
return _resolve_finalize_success(
|
|
723
|
+
return _resolve_finalize_success(
|
|
724
|
+
trimmed,
|
|
725
|
+
hint_kind,
|
|
726
|
+
capped,
|
|
727
|
+
graph=g,
|
|
728
|
+
microservice=microservice,
|
|
729
|
+
module=module,
|
|
730
|
+
)
|
|
664
731
|
except Exception as exc:
|
|
665
732
|
out = ResolveOutput(
|
|
666
733
|
success=False,
|
|
File without changes
|
|
@@ -20,7 +20,7 @@ from typing import Iterable
|
|
|
20
20
|
|
|
21
21
|
import tree_sitter_java as _ts_java
|
|
22
22
|
|
|
23
|
-
from brownfield_events import (
|
|
23
|
+
from java_codebase_rag.ast.brownfield_events import (
|
|
24
24
|
emit_brownfield_exclusivity_shadowing,
|
|
25
25
|
emit_brownfield_method_string_literal,
|
|
26
26
|
)
|
|
@@ -84,7 +84,7 @@ _DTO_LOMBOK_ANNOTATIONS: frozenset[str] = frozenset({
|
|
|
84
84
|
# Phase 11: `EDGE_SCHEMA` in `java_ontology.py` (canonical edge navigation schema; v14 re-index).
|
|
85
85
|
# Phase 12: CALLS `callee_declaring_role`, supertype-walk dedup, pass3 unresolved counters (v15 re-index).
|
|
86
86
|
# Bumps whenever extraction / enrichment semantics change.
|
|
87
|
-
ONTOLOGY_VERSION =
|
|
87
|
+
ONTOLOGY_VERSION = 18
|
|
88
88
|
|
|
89
89
|
ROLE_ANNOTATIONS: dict[str, str] = {
|
|
90
90
|
# Spring Web
|
|
@@ -1617,7 +1617,7 @@ def _parse_codebase_http_client_annotation(
|
|
|
1617
1617
|
val, vkind = _annotation_value(pairs["clientKind"], src)
|
|
1618
1618
|
if val and vkind == "enum":
|
|
1619
1619
|
kind_val = str(val)
|
|
1620
|
-
from java_ontology import VALID_CLIENT_KINDS # deferred: java_ontology imports ast_java
|
|
1620
|
+
from java_codebase_rag.graph.java_ontology import VALID_CLIENT_KINDS # deferred: java_ontology imports ast_java
|
|
1621
1621
|
if kind_val in VALID_CLIENT_KINDS:
|
|
1622
1622
|
client_kind = kind_val
|
|
1623
1623
|
else:
|
|
@@ -1697,7 +1697,7 @@ def _parse_codebase_producer_annotation(
|
|
|
1697
1697
|
val, vkind = _annotation_value(kind_node, src)
|
|
1698
1698
|
if val and vkind == "enum":
|
|
1699
1699
|
kind_val = str(val)
|
|
1700
|
-
from java_ontology import VALID_PRODUCER_KINDS # deferred: java_ontology imports ast_java
|
|
1700
|
+
from java_codebase_rag.graph.java_ontology import VALID_PRODUCER_KINDS # deferred: java_ontology imports ast_java
|
|
1701
1701
|
if kind_val in VALID_PRODUCER_KINDS:
|
|
1702
1702
|
client_kind = kind_val
|
|
1703
1703
|
else:
|
|
@@ -1868,7 +1868,7 @@ def _collect_outgoing_calls(
|
|
|
1868
1868
|
file_rel: str,
|
|
1869
1869
|
) -> list[OutgoingCallDecl]:
|
|
1870
1870
|
del project_root
|
|
1871
|
-
from java_ontology import ( # deferred: java_ontology imports ast_java
|
|
1871
|
+
from java_codebase_rag.graph.java_ontology import ( # deferred: java_ontology imports ast_java
|
|
1872
1872
|
CLIENT_KIND_FEIGN_METHOD,
|
|
1873
1873
|
CLIENT_KIND_REST_TEMPLATE,
|
|
1874
1874
|
CLIENT_KIND_WEB_CLIENT,
|
java_codebase_rag/cli.py
CHANGED
|
@@ -27,6 +27,7 @@ from java_codebase_rag.config import (
|
|
|
27
27
|
from java_codebase_rag._fdlimit import raise_fd_limit
|
|
28
28
|
from java_codebase_rag._version import version_string
|
|
29
29
|
from java_codebase_rag.pipeline import (
|
|
30
|
+
VECTORS_SKIPPED_GRAPH_ONLY as _VECTORS_SKIPPED_GRAPH_ONLY,
|
|
30
31
|
clip,
|
|
31
32
|
is_cocoindex_preflight_blocker,
|
|
32
33
|
is_graph_preflight_blocker,
|
|
@@ -35,7 +36,7 @@ from java_codebase_rag.pipeline import (
|
|
|
35
36
|
run_cocoindex_update,
|
|
36
37
|
run_incremental_graph,
|
|
37
38
|
)
|
|
38
|
-
from java_ontology import VALID_UNRESOLVED_CALL_REASONS
|
|
39
|
+
from java_codebase_rag.graph.java_ontology import VALID_UNRESOLVED_CALL_REASONS
|
|
39
40
|
|
|
40
41
|
LADYBUG_INCREMENTAL_TRACKING_ISSUE_URL = "https://github.com/HumanBean17/java-codebase-rag/issues/73"
|
|
41
42
|
|
|
@@ -72,12 +73,6 @@ def _reprocess_drift_graph_only_line(index_dir: Path) -> str:
|
|
|
72
73
|
)
|
|
73
74
|
|
|
74
75
|
|
|
75
|
-
_VECTORS_SKIPPED_GRAPH_ONLY = (
|
|
76
|
-
"java-codebase-rag: vectors skipped — vector stack not installed on this platform "
|
|
77
|
-
"(graph-only mode). The graph is built/refreshed; semantic search is unavailable."
|
|
78
|
-
)
|
|
79
|
-
|
|
80
|
-
|
|
81
76
|
def _reprocess_exit_code(payload: dict[str, Any]) -> int:
|
|
82
77
|
if payload.get("success"):
|
|
83
78
|
return 0
|
|
@@ -607,7 +602,7 @@ def _cmd_reprocess(args: argparse.Namespace) -> int:
|
|
|
607
602
|
_emit_reprocess_outcome(payload, selective_tty_mode="graph" if ok else None)
|
|
608
603
|
return _reprocess_exit_code(payload)
|
|
609
604
|
|
|
610
|
-
import server # lazy: pulls sentence_transformers/torch/lancedb/ladybug
|
|
605
|
+
from java_codebase_rag.mcp import server # lazy: pulls sentence_transformers/torch/lancedb/ladybug
|
|
611
606
|
|
|
612
607
|
result = asyncio.run(
|
|
613
608
|
server.run_refresh_pipeline(
|
|
@@ -682,7 +677,7 @@ def _cmd_erase(args: argparse.Namespace) -> int:
|
|
|
682
677
|
# tree_sitter (~54ms), and these filenames are only needed on the erase path.
|
|
683
678
|
# Keeping it out of the top-level import lets `java-codebase-rag --help` (and
|
|
684
679
|
# every other command) stay fast -- see the lazy-import invariant atop this file.
|
|
685
|
-
from build_ast_graph import BUILDER_OWNED_INDEX_FILES
|
|
680
|
+
from java_codebase_rag.graph.build_ast_graph import BUILDER_OWNED_INDEX_FILES
|
|
686
681
|
builder_paths = [cfg.ladybug_path.parent / name for name in BUILDER_OWNED_INDEX_FILES]
|
|
687
682
|
operator_paths = [cfg.index_dir / name for name in OPERATOR_OWNED_INDEX_FILES]
|
|
688
683
|
to_describe: list[Path] = [
|
|
@@ -773,12 +768,12 @@ def _cmd_erase(args: argparse.Namespace) -> int:
|
|
|
773
768
|
|
|
774
769
|
|
|
775
770
|
def _cmd_meta(args: argparse.Namespace) -> int:
|
|
776
|
-
import server # lazy
|
|
771
|
+
from java_codebase_rag.mcp import server # lazy
|
|
777
772
|
|
|
778
773
|
cfg = _resolved_from_ns(args)
|
|
779
774
|
_startup_hints(cfg)
|
|
780
775
|
cfg.apply_to_os_environ()
|
|
781
|
-
from ladybug_queries import LadybugGraph # lazy
|
|
776
|
+
from java_codebase_rag.graph.ladybug_queries import LadybugGraph # lazy
|
|
782
777
|
|
|
783
778
|
LadybugGraph._instance = None
|
|
784
779
|
LadybugGraph._instance_path = None
|
|
@@ -797,7 +792,7 @@ def _cmd_meta(args: argparse.Namespace) -> int:
|
|
|
797
792
|
|
|
798
793
|
|
|
799
794
|
def _cmd_tables(args: argparse.Namespace) -> int:
|
|
800
|
-
import server # lazy
|
|
795
|
+
from java_codebase_rag.mcp import server # lazy
|
|
801
796
|
|
|
802
797
|
cfg = _resolved_from_ns(args)
|
|
803
798
|
_startup_hints(cfg)
|
|
@@ -808,8 +803,8 @@ def _cmd_tables(args: argparse.Namespace) -> int:
|
|
|
808
803
|
|
|
809
804
|
|
|
810
805
|
def _cmd_diagnose_ignore(args: argparse.Namespace) -> int:
|
|
811
|
-
import server # lazy
|
|
812
|
-
from path_filtering import LayeredIgnore # lazy
|
|
806
|
+
from java_codebase_rag.mcp import server # lazy
|
|
807
|
+
from java_codebase_rag.graph.path_filtering import LayeredIgnore # lazy
|
|
813
808
|
|
|
814
809
|
cfg = _resolved_from_ns(args)
|
|
815
810
|
_startup_hints(cfg)
|
|
@@ -838,7 +833,7 @@ def _cmd_unresolved_calls_list(args: argparse.Namespace) -> int:
|
|
|
838
833
|
cfg = _resolved_from_ns(args)
|
|
839
834
|
_startup_hints(cfg)
|
|
840
835
|
cfg.apply_to_os_environ()
|
|
841
|
-
from ladybug_queries import LadybugGraph # lazy
|
|
836
|
+
from java_codebase_rag.graph.ladybug_queries import LadybugGraph # lazy
|
|
842
837
|
|
|
843
838
|
if not LadybugGraph.exists():
|
|
844
839
|
_emit({"success": False, "message": "LadybugDB graph not found"})
|
|
@@ -859,7 +854,7 @@ def _cmd_unresolved_calls_stats(args: argparse.Namespace) -> int:
|
|
|
859
854
|
cfg = _resolved_from_ns(args)
|
|
860
855
|
_startup_hints(cfg)
|
|
861
856
|
cfg.apply_to_os_environ()
|
|
862
|
-
from ladybug_queries import LadybugGraph # lazy
|
|
857
|
+
from java_codebase_rag.graph.ladybug_queries import LadybugGraph # lazy
|
|
863
858
|
|
|
864
859
|
if not LadybugGraph.exists():
|
|
865
860
|
_emit({"success": False, "message": "LadybugDB graph not found"})
|
|
@@ -883,8 +878,8 @@ def _cmd_analyze_pr(args: argparse.Namespace) -> int:
|
|
|
883
878
|
if not diff_text.strip():
|
|
884
879
|
_emit({"success": False, "message": "Diff is empty"})
|
|
885
880
|
return 1
|
|
886
|
-
import pr_analysis # lazy
|
|
887
|
-
from ladybug_queries import LadybugGraph # lazy
|
|
881
|
+
from java_codebase_rag.analysis import pr_analysis # lazy
|
|
882
|
+
from java_codebase_rag.graph.ladybug_queries import LadybugGraph # lazy
|
|
888
883
|
|
|
889
884
|
if not LadybugGraph.exists():
|
|
890
885
|
_emit({"success": False, "message": "LadybugDB graph not found"})
|
java_codebase_rag/config.py
CHANGED
|
@@ -363,6 +363,17 @@ class ResolvedOperatorConfig:
|
|
|
363
363
|
embedding_model_source: SettingSource
|
|
364
364
|
embedding_device_source: SettingSource
|
|
365
365
|
hints_enabled_source: SettingSource
|
|
366
|
+
# Absence diagnosis config knobs (PR-ABS-0)
|
|
367
|
+
absence_close_threshold: float
|
|
368
|
+
absence_absent_floor: float
|
|
369
|
+
absence_candidate_count: int
|
|
370
|
+
absence_ngram_q: int
|
|
371
|
+
absence_diag_enabled: bool
|
|
372
|
+
absence_close_threshold_source: SettingSource
|
|
373
|
+
absence_absent_floor_source: SettingSource
|
|
374
|
+
absence_candidate_count_source: SettingSource
|
|
375
|
+
absence_ngram_q_source: SettingSource
|
|
376
|
+
absence_diag_enabled_source: SettingSource
|
|
366
377
|
# Absolute path of the YAML actually loaded (None when built-in defaults were
|
|
367
378
|
# used with no config file). Recorded into the index dir at index time so a
|
|
368
379
|
# later discovery run from a sibling/cwd can relocate this config.
|
|
@@ -380,6 +391,8 @@ class ResolvedOperatorConfig:
|
|
|
380
391
|
os.environ["SBERT_MODEL"] = self.embedding_model
|
|
381
392
|
if self.embedding_device is not None:
|
|
382
393
|
os.environ["SBERT_DEVICE"] = self.embedding_device
|
|
394
|
+
# Publish absence diagnosis knobs for subprocess builds (PR-ABS-1)
|
|
395
|
+
os.environ["JAVA_CODEBASE_RAG_ABSENCE_NGRAM_Q"] = str(self.absence_ngram_q)
|
|
383
396
|
|
|
384
397
|
def subprocess_env(self, base: dict[str, str] | None = None) -> dict[str, str]:
|
|
385
398
|
out = dict(base or os.environ)
|
|
@@ -390,6 +403,8 @@ class ResolvedOperatorConfig:
|
|
|
390
403
|
out["SBERT_DEVICE"] = self.embedding_device
|
|
391
404
|
else:
|
|
392
405
|
out.pop("SBERT_DEVICE", None)
|
|
406
|
+
# Publish absence diagnosis knobs for subprocess builds (PR-ABS-1)
|
|
407
|
+
out["JAVA_CODEBASE_RAG_ABSENCE_NGRAM_Q"] = str(self.absence_ngram_q)
|
|
393
408
|
return out
|
|
394
409
|
|
|
395
410
|
|
|
@@ -459,6 +474,66 @@ def _pick_bool(
|
|
|
459
474
|
return default, "default"
|
|
460
475
|
|
|
461
476
|
|
|
477
|
+
def _pick_float(
|
|
478
|
+
*,
|
|
479
|
+
env_key: str,
|
|
480
|
+
yaml_dict: dict[str, Any],
|
|
481
|
+
yaml_path: tuple[str, ...],
|
|
482
|
+
default: float,
|
|
483
|
+
) -> tuple[float, SettingSource]:
|
|
484
|
+
"""Pick a float setting from env (parsed via float(...)), YAML, or default.
|
|
485
|
+
|
|
486
|
+
Precedence: CLI > env > YAML > default. Env values that fail to parse as float
|
|
487
|
+
fall back to the default (matching the brief's requirement for graceful degradation).
|
|
488
|
+
"""
|
|
489
|
+
env_raw = os.environ.get(env_key, "").strip()
|
|
490
|
+
if env_raw:
|
|
491
|
+
try:
|
|
492
|
+
return float(env_raw), "env"
|
|
493
|
+
except ValueError:
|
|
494
|
+
# Invalid env value falls back to default (per brief)
|
|
495
|
+
pass
|
|
496
|
+
cur: Any = yaml_dict
|
|
497
|
+
for part in yaml_path:
|
|
498
|
+
if not isinstance(cur, dict) or part not in cur:
|
|
499
|
+
cur = None
|
|
500
|
+
break
|
|
501
|
+
cur = cur.get(part)
|
|
502
|
+
if isinstance(cur, (int, float)):
|
|
503
|
+
return float(cur), "yaml"
|
|
504
|
+
return default, "default"
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
def _pick_int(
|
|
508
|
+
*,
|
|
509
|
+
env_key: str,
|
|
510
|
+
yaml_dict: dict[str, Any],
|
|
511
|
+
yaml_path: tuple[str, ...],
|
|
512
|
+
default: int,
|
|
513
|
+
) -> tuple[int, SettingSource]:
|
|
514
|
+
"""Pick an int setting from env (parsed via int(...)), YAML, or default.
|
|
515
|
+
|
|
516
|
+
Precedence: CLI > env > YAML > default. Env values that fail to parse as int
|
|
517
|
+
fall back to the default (matching the brief's requirement for graceful degradation).
|
|
518
|
+
"""
|
|
519
|
+
env_raw = os.environ.get(env_key, "").strip()
|
|
520
|
+
if env_raw:
|
|
521
|
+
try:
|
|
522
|
+
return int(env_raw), "env"
|
|
523
|
+
except ValueError:
|
|
524
|
+
# Invalid env value falls back to default (per brief)
|
|
525
|
+
pass
|
|
526
|
+
cur: Any = yaml_dict
|
|
527
|
+
for part in yaml_path:
|
|
528
|
+
if not isinstance(cur, dict) or part not in cur:
|
|
529
|
+
cur = None
|
|
530
|
+
break
|
|
531
|
+
cur = cur.get(part)
|
|
532
|
+
if isinstance(cur, int):
|
|
533
|
+
return cur, "yaml"
|
|
534
|
+
return default, "default"
|
|
535
|
+
|
|
536
|
+
|
|
462
537
|
def _resolve_index_dir_path(
|
|
463
538
|
*,
|
|
464
539
|
source_root: Path,
|
|
@@ -565,6 +640,37 @@ def resolve_operator_config(
|
|
|
565
640
|
yaml_path=("hints", "enabled"),
|
|
566
641
|
default=True,
|
|
567
642
|
)
|
|
643
|
+
# Absence diagnosis config (PR-ABS-0)
|
|
644
|
+
abs_close, abs_close_src = _pick_float(
|
|
645
|
+
env_key="JAVA_CODEBASE_RAG_ABSENCE_CLOSE_THRESHOLD",
|
|
646
|
+
yaml_dict=yaml_dict,
|
|
647
|
+
yaml_path=("absence", "close_threshold"),
|
|
648
|
+
default=0.85,
|
|
649
|
+
)
|
|
650
|
+
abs_floor, abs_floor_src = _pick_float(
|
|
651
|
+
env_key="JAVA_CODEBASE_RAG_ABSENCE_ABSENT_FLOOR",
|
|
652
|
+
yaml_dict=yaml_dict,
|
|
653
|
+
yaml_path=("absence", "absent_floor"),
|
|
654
|
+
default=0.40,
|
|
655
|
+
)
|
|
656
|
+
abs_cand, abs_cand_src = _pick_int(
|
|
657
|
+
env_key="JAVA_CODEBASE_RAG_ABSENCE_CANDIDATE_COUNT",
|
|
658
|
+
yaml_dict=yaml_dict,
|
|
659
|
+
yaml_path=("absence", "candidate_count"),
|
|
660
|
+
default=5,
|
|
661
|
+
)
|
|
662
|
+
abs_q, abs_q_src = _pick_int(
|
|
663
|
+
env_key="JAVA_CODEBASE_RAG_ABSENCE_NGRAM_Q",
|
|
664
|
+
yaml_dict=yaml_dict,
|
|
665
|
+
yaml_path=("absence", "ngram_q"),
|
|
666
|
+
default=3,
|
|
667
|
+
)
|
|
668
|
+
abs_diag, abs_diag_src = _pick_bool(
|
|
669
|
+
env_key="JAVA_CODEBASE_RAG_ABSENCE_DIAG_ENABLED",
|
|
670
|
+
yaml_dict=yaml_dict,
|
|
671
|
+
yaml_path=("absence", "diag_enabled"),
|
|
672
|
+
default=True,
|
|
673
|
+
)
|
|
568
674
|
ku = index_dir / "code_graph.lbug"
|
|
569
675
|
coco = index_dir / "cocoindex.db"
|
|
570
676
|
return ResolvedOperatorConfig(
|
|
@@ -579,6 +685,16 @@ def resolve_operator_config(
|
|
|
579
685
|
embedding_model_source=model_src,
|
|
580
686
|
embedding_device_source=device_src,
|
|
581
687
|
hints_enabled_source=hints_src,
|
|
688
|
+
absence_close_threshold=abs_close,
|
|
689
|
+
absence_absent_floor=abs_floor,
|
|
690
|
+
absence_candidate_count=abs_cand,
|
|
691
|
+
absence_ngram_q=abs_q,
|
|
692
|
+
absence_diag_enabled=abs_diag,
|
|
693
|
+
absence_close_threshold_source=abs_close_src,
|
|
694
|
+
absence_absent_floor_source=abs_floor_src,
|
|
695
|
+
absence_candidate_count_source=abs_cand_src,
|
|
696
|
+
absence_ngram_q_source=abs_q_src,
|
|
697
|
+
absence_diag_enabled_source=abs_diag_src,
|
|
582
698
|
yaml_config_path=find_yaml_config_file(config_dir),
|
|
583
699
|
)
|
|
584
700
|
|
|
File without changes
|