java-codebase-rag 0.6.1__py3-none-any.whl → 0.6.3__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.
- build_ast_graph.py +95 -7
- java_codebase_rag/cli.py +105 -15
- java_codebase_rag/cli_format.py +8 -35
- java_codebase_rag/cli_progress.py +17 -21
- java_codebase_rag/config.py +54 -5
- java_codebase_rag/installer.py +192 -45
- java_codebase_rag/lance_optimize.py +125 -51
- java_codebase_rag/pipeline.py +71 -23
- java_codebase_rag/progress.py +570 -0
- {java_codebase_rag-0.6.1.dist-info → java_codebase_rag-0.6.3.dist-info}/METADATA +4 -1
- java_codebase_rag-0.6.3.dist-info/RECORD +34 -0
- java_codebase_rag-0.6.3.dist-info/entry_points.txt +3 -0
- java_index_flow_lancedb.py +155 -0
- server.py +75 -9
- java_codebase_rag-0.6.1.dist-info/RECORD +0 -36
- java_codebase_rag-0.6.1.dist-info/entry_points.txt +0 -3
- kuzu_queries.py +0 -1989
- user_rag/__init__.py +0 -1
- user_rag/cli.py +0 -175
- {java_codebase_rag-0.6.1.dist-info → java_codebase_rag-0.6.3.dist-info}/WHEEL +0 -0
- {java_codebase_rag-0.6.1.dist-info → java_codebase_rag-0.6.3.dist-info}/licenses/LICENSE +0 -0
- {java_codebase_rag-0.6.1.dist-info → java_codebase_rag-0.6.3.dist-info}/top_level.txt +0 -0
java_codebase_rag/installer.py
CHANGED
|
@@ -14,6 +14,7 @@ import os
|
|
|
14
14
|
import shutil
|
|
15
15
|
import sys
|
|
16
16
|
import tempfile
|
|
17
|
+
import time
|
|
17
18
|
from dataclasses import dataclass
|
|
18
19
|
from pathlib import Path
|
|
19
20
|
from typing import Literal, NamedTuple
|
|
@@ -759,6 +760,11 @@ def generate_yaml_config(
|
|
|
759
760
|
else:
|
|
760
761
|
config["embedding"].pop("model", None)
|
|
761
762
|
|
|
763
|
+
# Seed cross-service resolution safe-by-default: only evidence-backed cross-service
|
|
764
|
+
# edges survive (see _is_brownfield_sourced in build_ast_graph). setdefault preserves
|
|
765
|
+
# an explicit user choice (e.g. `auto`) on re-run update.
|
|
766
|
+
config.setdefault("cross_service_resolution", "brownfield_only")
|
|
767
|
+
|
|
762
768
|
# Keys NOT written by installer (preserved if present):
|
|
763
769
|
# - source_root (config.py resolves from walk-up discovery)
|
|
764
770
|
# - index_dir (config.py defaults to <source_root>/.java-codebase-rag)
|
|
@@ -801,6 +807,38 @@ def update_gitignore(cwd: Path) -> None:
|
|
|
801
807
|
gitignore_path.write_text("\n".join(lines), encoding="utf-8")
|
|
802
808
|
|
|
803
809
|
|
|
810
|
+
def _index_progress_header(subcommand: str, source_root: Path, index_dir: Path) -> None:
|
|
811
|
+
"""Print the stderr header framing the indexing sub-step (install/update).
|
|
812
|
+
|
|
813
|
+
Mirrors the operator commands' ``_pipeline_header`` but lives in the
|
|
814
|
+
installer because the wizard's stdout framing differs. This brackets ONLY
|
|
815
|
+
the indexing sub-step — the wizard's prompts stay outside it on stdout.
|
|
816
|
+
"""
|
|
817
|
+
from java_codebase_rag.cli_format import bold
|
|
818
|
+
|
|
819
|
+
print(
|
|
820
|
+
bold(
|
|
821
|
+
f"java-codebase-rag {subcommand} · source={source_root.resolve()} "
|
|
822
|
+
f"· index={index_dir.resolve()}"
|
|
823
|
+
),
|
|
824
|
+
file=sys.stderr,
|
|
825
|
+
flush=True,
|
|
826
|
+
)
|
|
827
|
+
|
|
828
|
+
|
|
829
|
+
def _index_progress_footer(subcommand: str, started: float, *, ok: bool) -> None:
|
|
830
|
+
"""Print the stderr footer closing the indexing sub-step framing."""
|
|
831
|
+
from java_codebase_rag.cli_format import bold, styled_check, styled_cross
|
|
832
|
+
|
|
833
|
+
elapsed = time.perf_counter() - started
|
|
834
|
+
marker = styled_check() if ok else styled_cross()
|
|
835
|
+
print(
|
|
836
|
+
f"{marker} {bold(f'java-codebase-rag {subcommand} · finished in {elapsed:.2f}s')}",
|
|
837
|
+
file=sys.stderr,
|
|
838
|
+
flush=True,
|
|
839
|
+
)
|
|
840
|
+
|
|
841
|
+
|
|
804
842
|
def run_init_if_needed(
|
|
805
843
|
source_root: Path,
|
|
806
844
|
index_dir: Path,
|
|
@@ -808,15 +846,25 @@ def run_init_if_needed(
|
|
|
808
846
|
*,
|
|
809
847
|
non_interactive: bool,
|
|
810
848
|
quiet: bool,
|
|
849
|
+
verbose: bool = False,
|
|
811
850
|
) -> bool:
|
|
812
851
|
"""Run init if index directory has no artifacts. Return True if init was run.
|
|
813
852
|
|
|
853
|
+
The indexing sub-step (CocoIndex update + AST graph build) renders the
|
|
854
|
+
unified ``Vectors → Optimize → Graph`` progress on **stderr** in default
|
|
855
|
+
mode (same renderer the operator commands use); the wizard's conversational
|
|
856
|
+
stdout is untouched by this function. ``--quiet`` is silent; ``--verbose``
|
|
857
|
+
raw-relays subprocess output. The indexing chatter that used to print to
|
|
858
|
+
stdout (``Creating index…`` / ``Index created successfully.``) now lives
|
|
859
|
+
on stderr framing so stdout stays the wizard payload.
|
|
860
|
+
|
|
814
861
|
Args:
|
|
815
862
|
source_root: Source root directory
|
|
816
863
|
index_dir: Index directory path
|
|
817
864
|
model: Embedding model path or "auto"
|
|
818
865
|
non_interactive: If True, suppress prompts
|
|
819
|
-
quiet: If True, suppress output
|
|
866
|
+
quiet: If True, suppress progress output
|
|
867
|
+
verbose: If True, raw-relay subprocess output (no Live region)
|
|
820
868
|
|
|
821
869
|
Returns:
|
|
822
870
|
True if init was run, False if skipped
|
|
@@ -832,36 +880,71 @@ def run_init_if_needed(
|
|
|
832
880
|
print("Index already exists. Run `java-codebase-rag reprocess` to rebuild.")
|
|
833
881
|
return False
|
|
834
882
|
|
|
835
|
-
print("Creating index...")
|
|
836
883
|
cfg = resolve_operator_config(
|
|
837
884
|
source_root=source_root,
|
|
838
885
|
cli_index_dir=None, # use default (<source_root>/.java-codebase-rag)
|
|
839
886
|
cli_embedding_model=model if model != "auto" else None,
|
|
840
887
|
)
|
|
841
888
|
cfg.apply_to_os_environ()
|
|
842
|
-
|
|
843
889
|
env = cfg.subprocess_env()
|
|
844
890
|
|
|
845
|
-
#
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
891
|
+
# Indexing sub-step: render unified progress on stderr in default mode only
|
|
892
|
+
# (quiet = silent; verbose = raw relay, no Live region). The renderer wraps
|
|
893
|
+
# just this sub-step, not the surrounding wizard.
|
|
894
|
+
on_progress, on_progress_console = None, None
|
|
895
|
+
renderer = None
|
|
896
|
+
if not quiet and not verbose:
|
|
897
|
+
from java_codebase_rag.progress import build_index_progress_context
|
|
898
|
+
|
|
899
|
+
renderer, on_progress, on_progress_console = build_index_progress_context()
|
|
900
|
+
|
|
901
|
+
started = time.perf_counter()
|
|
902
|
+
if renderer is not None:
|
|
903
|
+
_index_progress_header("install", cfg.source_root, cfg.index_dir)
|
|
904
|
+
renderer.start()
|
|
905
|
+
index_ok = True
|
|
906
|
+
try:
|
|
907
|
+
coco = run_cocoindex_update(
|
|
908
|
+
env,
|
|
909
|
+
full_reprocess=False,
|
|
910
|
+
quiet=quiet,
|
|
911
|
+
verbose=verbose,
|
|
912
|
+
on_progress=on_progress,
|
|
913
|
+
on_progress_console=on_progress_console,
|
|
914
|
+
)
|
|
915
|
+
if coco.returncode != 0:
|
|
916
|
+
print(
|
|
917
|
+
f"Error: CocoIndex update failed with code {coco.returncode}",
|
|
918
|
+
file=sys.stderr,
|
|
919
|
+
)
|
|
920
|
+
index_ok = False
|
|
921
|
+
else:
|
|
922
|
+
g = run_build_ast_graph(
|
|
923
|
+
source_root=cfg.source_root,
|
|
924
|
+
ladybug_path=cfg.ladybug_path,
|
|
925
|
+
verbose=verbose,
|
|
926
|
+
quiet=quiet,
|
|
927
|
+
env=env,
|
|
928
|
+
on_progress=on_progress,
|
|
929
|
+
on_progress_console=on_progress_console,
|
|
930
|
+
)
|
|
931
|
+
if g.returncode != 0:
|
|
932
|
+
print(
|
|
933
|
+
f"Error: AST graph build failed with code {g.returncode}",
|
|
934
|
+
file=sys.stderr,
|
|
935
|
+
)
|
|
936
|
+
index_ok = False
|
|
937
|
+
except BaseException:
|
|
938
|
+
# An exception from cocoindex/graph means the index did not succeed;
|
|
939
|
+
# flip the footer marker before re-raising so it renders a red cross
|
|
940
|
+
# (mirrors cli._run_with_pipeline_progress's BaseException handler).
|
|
941
|
+
index_ok = False
|
|
942
|
+
raise
|
|
943
|
+
finally:
|
|
944
|
+
if renderer is not None:
|
|
945
|
+
renderer.stop()
|
|
946
|
+
_index_progress_footer("install", started, ok=index_ok)
|
|
947
|
+
return index_ok
|
|
865
948
|
|
|
866
949
|
|
|
867
950
|
def handle_rerun(cwd: Path, *, non_interactive: bool) -> dict | None:
|
|
@@ -1196,13 +1279,25 @@ def run_update(
|
|
|
1196
1279
|
force: bool,
|
|
1197
1280
|
dry_run: bool,
|
|
1198
1281
|
cwd: Path | None = None,
|
|
1282
|
+
quiet: bool = False,
|
|
1283
|
+
verbose: bool = False,
|
|
1199
1284
|
) -> int:
|
|
1200
1285
|
"""Run the update pipeline. Returns exit code.
|
|
1201
1286
|
|
|
1287
|
+
The indexing sub-step (Lance catch-up + incremental graph) renders the
|
|
1288
|
+
unified ``Vectors → Optimize → Graph`` progress on **stderr** in default
|
|
1289
|
+
mode and no longer runs with ``quiet=True`` (the reason ``update`` was
|
|
1290
|
+
silent). ``--quiet`` is silent; ``--verbose`` raw-relays subprocess output.
|
|
1291
|
+
The wizard's host-detection / refresh / summary stdout is preserved; only
|
|
1292
|
+
the indexing chatter that used to print to stdout moves onto the stderr
|
|
1293
|
+
renderer framing.
|
|
1294
|
+
|
|
1202
1295
|
Args:
|
|
1203
1296
|
force: If True, overwrite all artifacts even if matching
|
|
1204
1297
|
dry_run: If True, print changes without writing
|
|
1205
1298
|
cwd: Current working directory (defaults to Path.cwd())
|
|
1299
|
+
quiet: If True, suppress progress output
|
|
1300
|
+
verbose: If True, raw-relay subprocess output (no Live region)
|
|
1206
1301
|
|
|
1207
1302
|
Returns:
|
|
1208
1303
|
Exit code (0=success, 1=partial, 2=fatal)
|
|
@@ -1250,9 +1345,14 @@ def run_update(
|
|
|
1250
1345
|
print("Skipping index update.")
|
|
1251
1346
|
return EXIT_PARTIAL if has_artifact_failures else EXIT_SUCCESS
|
|
1252
1347
|
|
|
1253
|
-
# Resolve configuration
|
|
1348
|
+
# Resolve configuration. Pass source_root=None so the YAML ``source_root``
|
|
1349
|
+
# field is honored exactly like increment/init/reprocess — passing the
|
|
1350
|
+
# discovered config dir here routes resolve_operator_config into the
|
|
1351
|
+
# explicit-override branch that SKIPS the YAML field, which made `update`
|
|
1352
|
+
# point cocoindex at the config dir (no Java) against the real index and
|
|
1353
|
+
# mass-delete it. Discovery still runs against the CLI's cwd.
|
|
1254
1354
|
try:
|
|
1255
|
-
cfg = resolve_operator_config(source_root=
|
|
1355
|
+
cfg = resolve_operator_config(source_root=None, cli_index_dir=None)
|
|
1256
1356
|
index_dir = cfg.index_dir
|
|
1257
1357
|
except Exception as e:
|
|
1258
1358
|
print(f"\nWarning: Failed to resolve configuration: {e}")
|
|
@@ -1272,30 +1372,74 @@ def run_update(
|
|
|
1272
1372
|
# The "graph not implemented" warning belongs only on the vectors-only path
|
|
1273
1373
|
# (increment --vectors-only), where the graph step is deliberately skipped.
|
|
1274
1374
|
if not dry_run:
|
|
1275
|
-
print("\nUpdating index (Lance + graph)...")
|
|
1276
1375
|
cfg.apply_to_os_environ()
|
|
1277
1376
|
env = cfg.subprocess_env()
|
|
1278
1377
|
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
)
|
|
1291
|
-
if
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1378
|
+
# Indexing sub-step: render unified progress on stderr in default mode
|
|
1379
|
+
# only (quiet = silent; verbose = raw relay). No longer runs quiet=True
|
|
1380
|
+
# — that was why `update` was silent. The renderer wraps just this
|
|
1381
|
+
# sub-step; the wizard's summary stdout below is outside it.
|
|
1382
|
+
on_progress, on_progress_console = None, None
|
|
1383
|
+
renderer = None
|
|
1384
|
+
if not quiet and not verbose:
|
|
1385
|
+
from java_codebase_rag.progress import build_index_progress_context
|
|
1386
|
+
|
|
1387
|
+
renderer, on_progress, on_progress_console = build_index_progress_context()
|
|
1388
|
+
|
|
1389
|
+
started = time.perf_counter()
|
|
1390
|
+
if renderer is not None:
|
|
1391
|
+
_index_progress_header("update", cfg.source_root, cfg.index_dir)
|
|
1392
|
+
renderer.start()
|
|
1393
|
+
index_ok = True
|
|
1394
|
+
try:
|
|
1395
|
+
coco = run_cocoindex_update(
|
|
1396
|
+
env,
|
|
1397
|
+
full_reprocess=False,
|
|
1398
|
+
quiet=quiet,
|
|
1399
|
+
verbose=verbose,
|
|
1400
|
+
on_progress=on_progress,
|
|
1401
|
+
on_progress_console=on_progress_console,
|
|
1298
1402
|
)
|
|
1403
|
+
if coco.returncode != 0:
|
|
1404
|
+
print(
|
|
1405
|
+
f"Error: Lance index update failed with code {coco.returncode}",
|
|
1406
|
+
file=sys.stderr,
|
|
1407
|
+
)
|
|
1408
|
+
index_ok = False
|
|
1409
|
+
else:
|
|
1410
|
+
g = run_incremental_graph(
|
|
1411
|
+
source_root=cfg.source_root,
|
|
1412
|
+
ladybug_path=cfg.ladybug_path,
|
|
1413
|
+
verbose=verbose,
|
|
1414
|
+
quiet=quiet,
|
|
1415
|
+
env=env,
|
|
1416
|
+
on_progress=on_progress,
|
|
1417
|
+
on_progress_console=on_progress_console,
|
|
1418
|
+
)
|
|
1419
|
+
if g.returncode != 0:
|
|
1420
|
+
# The graph catch-up is best-effort: `update`'s primary job
|
|
1421
|
+
# is refreshing shipped artifacts + vectors (cocoindex). A
|
|
1422
|
+
# graph failure surfaces a truthful, actionable Warning on
|
|
1423
|
+
# stderr but does NOT flip index_ok (which drives both the
|
|
1424
|
+
# footer marker and the return code) — exit 0 with a green
|
|
1425
|
+
# check + the Warning line carrying the graph caveat.
|
|
1426
|
+
print(
|
|
1427
|
+
f"\nWarning: incremental graph update failed (exit {g.returncode}). "
|
|
1428
|
+
"Run `java-codebase-rag reprocess` for a full rebuild.",
|
|
1429
|
+
file=sys.stderr,
|
|
1430
|
+
)
|
|
1431
|
+
except BaseException:
|
|
1432
|
+
# An exception from cocoindex/graph means the index did not succeed;
|
|
1433
|
+
# flip the footer marker before re-raising so it renders a red cross
|
|
1434
|
+
# (mirrors cli._run_with_pipeline_progress's BaseException handler).
|
|
1435
|
+
index_ok = False
|
|
1436
|
+
raise
|
|
1437
|
+
finally:
|
|
1438
|
+
if renderer is not None:
|
|
1439
|
+
renderer.stop()
|
|
1440
|
+
_index_progress_footer("update", started, ok=index_ok)
|
|
1441
|
+
if not index_ok:
|
|
1442
|
+
return 1
|
|
1299
1443
|
else:
|
|
1300
1444
|
print("\nWould run incremental index update (Lance + graph).")
|
|
1301
1445
|
|
|
@@ -1315,6 +1459,7 @@ def run_install(
|
|
|
1315
1459
|
model: str | None,
|
|
1316
1460
|
source_root: Path | None = None,
|
|
1317
1461
|
quiet: bool = False,
|
|
1462
|
+
verbose: bool = False,
|
|
1318
1463
|
) -> int:
|
|
1319
1464
|
"""Run the install pipeline. Returns exit code.
|
|
1320
1465
|
|
|
@@ -1325,6 +1470,7 @@ def run_install(
|
|
|
1325
1470
|
model: Model from CLI flag
|
|
1326
1471
|
source_root: Source root path (defaults to cwd if None)
|
|
1327
1472
|
quiet: If True, suppress output
|
|
1473
|
+
verbose: If True, raw-relay subprocess indexing output (no Live region)
|
|
1328
1474
|
|
|
1329
1475
|
Returns:
|
|
1330
1476
|
Exit code (0=success, 1=partial, 2=fatal)
|
|
@@ -1423,6 +1569,7 @@ def run_install(
|
|
|
1423
1569
|
resolved_model,
|
|
1424
1570
|
non_interactive=non_interactive,
|
|
1425
1571
|
quiet=quiet,
|
|
1572
|
+
verbose=verbose,
|
|
1426
1573
|
)
|
|
1427
1574
|
|
|
1428
1575
|
return 0
|
|
@@ -21,7 +21,14 @@ from __future__ import annotations
|
|
|
21
21
|
|
|
22
22
|
import asyncio
|
|
23
23
|
import sys
|
|
24
|
+
import time
|
|
24
25
|
from pathlib import Path
|
|
26
|
+
from typing import Callable, Literal
|
|
27
|
+
|
|
28
|
+
# Mirrors ``ProgressStatus`` in ``progress.py``; kept local (rather than imported)
|
|
29
|
+
# so this module never pays the ``rich`` cost at import time — see
|
|
30
|
+
# ``_make_optimize_event``.
|
|
31
|
+
_OptimizeStatus = Literal["running", "done", "failed"]
|
|
25
32
|
|
|
26
33
|
# Single source of truth for the three Lance table names created by the flow.
|
|
27
34
|
# Keep in sync with ``search_lancedb.TABLES`` (the values there mirror these).
|
|
@@ -31,6 +38,33 @@ LANCE_TABLE_NAMES: tuple[str, ...] = (
|
|
|
31
38
|
"yamlconfigindex_yaml_config",
|
|
32
39
|
)
|
|
33
40
|
|
|
41
|
+
|
|
42
|
+
def _make_optimize_event(
|
|
43
|
+
*,
|
|
44
|
+
status: _OptimizeStatus,
|
|
45
|
+
elapsed_s: float | None = None,
|
|
46
|
+
):
|
|
47
|
+
"""Build a ``ProgressEvent(kind="optimize", …)`` lazily (progress is parent-side).
|
|
48
|
+
|
|
49
|
+
``lance_optimize`` runs in-process in the parent (called by
|
|
50
|
+
``pipeline._maybe_run_serialized_optimize`` and
|
|
51
|
+
``server.run_refresh_pipeline``); it routes progress to the renderer via the
|
|
52
|
+
in-process ``on_progress`` callback — NOT via stderr (which would corrupt
|
|
53
|
+
the Live region). The import is local so the flow (which imports
|
|
54
|
+
``LANCE_TABLE_NAMES`` at definition time) never pays the ``rich`` cost.
|
|
55
|
+
"""
|
|
56
|
+
from java_codebase_rag.progress import ProgressEvent
|
|
57
|
+
|
|
58
|
+
return ProgressEvent(
|
|
59
|
+
kind="optimize",
|
|
60
|
+
phase=None,
|
|
61
|
+
pass_=None,
|
|
62
|
+
done=None,
|
|
63
|
+
total=None,
|
|
64
|
+
status=status,
|
|
65
|
+
elapsed_s=elapsed_s,
|
|
66
|
+
)
|
|
67
|
+
|
|
34
68
|
# Commit conflicts are transient; a handful of exponential-backoff retries is
|
|
35
69
|
# enough because, post-flow, there are no concurrent writers — only successive
|
|
36
70
|
# optimize/compaction passes within this single serialized call can still
|
|
@@ -60,7 +94,12 @@ async def _list_table_names(db: object) -> set[str]:
|
|
|
60
94
|
return set(await db.table_names())
|
|
61
95
|
|
|
62
96
|
|
|
63
|
-
async def optimize_lance_tables(
|
|
97
|
+
async def optimize_lance_tables(
|
|
98
|
+
index_dir: Path,
|
|
99
|
+
*,
|
|
100
|
+
quiet: bool = False,
|
|
101
|
+
on_progress: Callable | None = None,
|
|
102
|
+
) -> dict[str, str]:
|
|
64
103
|
"""Optimize all known Lance tables under *index_dir*, serially, with retry.
|
|
65
104
|
|
|
66
105
|
Runs ``table.optimize()`` for each name in :data:`LANCE_TABLE_NAMES` that
|
|
@@ -73,6 +112,13 @@ async def optimize_lance_tables(index_dir: Path, *, quiet: bool = False) -> dict
|
|
|
73
112
|
index_dir: directory holding the Lance tables (the flow's LanceDB URI).
|
|
74
113
|
quiet: when True, suppress the per-table success/skip info lines on
|
|
75
114
|
stderr (errors are always logged).
|
|
115
|
+
on_progress: optional in-process progress callback (the parent's
|
|
116
|
+
renderer ``on_progress``). When given, emits
|
|
117
|
+
``ProgressEvent(kind="optimize", status="running")`` on entry and a
|
|
118
|
+
terminal ``status="done"``/``"failed"`` event on exit (covers BOTH
|
|
119
|
+
call sites: ``pipeline._maybe_run_serialized_optimize`` and
|
|
120
|
+
``server.run_refresh_pipeline``). In-process only — NEVER prints to
|
|
121
|
+
stderr (that would corrupt the Live region).
|
|
76
122
|
|
|
77
123
|
Returns:
|
|
78
124
|
Mapping of table name → status. Values are ``"ok"``, ``"skipped"``
|
|
@@ -82,67 +128,95 @@ async def optimize_lance_tables(index_dir: Path, *, quiet: bool = False) -> dict
|
|
|
82
128
|
# not pay the lancedb import cost at flow-definition time.
|
|
83
129
|
import lancedb
|
|
84
130
|
|
|
131
|
+
if on_progress is not None:
|
|
132
|
+
on_progress(_make_optimize_event(status="running"))
|
|
133
|
+
t0 = time.perf_counter()
|
|
85
134
|
results: dict[str, str] = {}
|
|
86
|
-
|
|
135
|
+
failed = False
|
|
87
136
|
try:
|
|
137
|
+
db = await lancedb.connect_async(str(index_dir))
|
|
88
138
|
try:
|
|
89
|
-
existing = await _list_table_names(db)
|
|
90
|
-
except Exception as exc:
|
|
91
|
-
print(
|
|
92
|
-
f"java-codebase-rag: optimize: failed to list tables in "
|
|
93
|
-
f"{index_dir}: {exc}",
|
|
94
|
-
file=sys.stderr,
|
|
95
|
-
)
|
|
96
|
-
return {name: f"error: list failed: {exc}" for name in LANCE_TABLE_NAMES}
|
|
97
|
-
|
|
98
|
-
for name in LANCE_TABLE_NAMES:
|
|
99
|
-
if name not in existing:
|
|
100
|
-
results[name] = "skipped"
|
|
101
|
-
if not quiet:
|
|
102
|
-
print(
|
|
103
|
-
f"java-codebase-rag: optimize: {name} absent, skipped",
|
|
104
|
-
file=sys.stderr,
|
|
105
|
-
)
|
|
106
|
-
continue
|
|
107
139
|
try:
|
|
108
|
-
|
|
140
|
+
existing = await _list_table_names(db)
|
|
109
141
|
except Exception as exc:
|
|
110
|
-
results[name] = f"error: open failed: {exc}"
|
|
111
142
|
print(
|
|
112
|
-
f"java-codebase-rag: optimize:
|
|
143
|
+
f"java-codebase-rag: optimize: failed to list tables in "
|
|
144
|
+
f"{index_dir}: {exc}",
|
|
113
145
|
file=sys.stderr,
|
|
114
146
|
)
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
for
|
|
147
|
+
failed = True
|
|
148
|
+
return {name: f"error: list failed: {exc}" for name in LANCE_TABLE_NAMES}
|
|
149
|
+
|
|
150
|
+
for name in LANCE_TABLE_NAMES:
|
|
151
|
+
if name not in existing:
|
|
152
|
+
results[name] = "skipped"
|
|
153
|
+
if not quiet:
|
|
154
|
+
print(
|
|
155
|
+
f"java-codebase-rag: optimize: {name} absent, skipped",
|
|
156
|
+
file=sys.stderr,
|
|
157
|
+
)
|
|
158
|
+
continue
|
|
119
159
|
try:
|
|
120
|
-
await
|
|
121
|
-
last_exc = None
|
|
122
|
-
break
|
|
160
|
+
table = await db.open_table(name)
|
|
123
161
|
except Exception as exc:
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
await asyncio.sleep(_BASE_BACKOFF_S * (2**attempt))
|
|
127
|
-
continue
|
|
128
|
-
# Non-retryable, or retries exhausted: stop the loop and
|
|
129
|
-
# surface below — do not swallow silently.
|
|
130
|
-
break
|
|
131
|
-
|
|
132
|
-
if last_exc is None:
|
|
133
|
-
results[name] = "ok"
|
|
134
|
-
if not quiet:
|
|
162
|
+
results[name] = f"error: open failed: {exc}"
|
|
163
|
+
failed = True
|
|
135
164
|
print(
|
|
136
|
-
f"java-codebase-rag: optimize: {name}
|
|
165
|
+
f"java-codebase-rag: optimize: {name} open failed: {exc}",
|
|
137
166
|
file=sys.stderr,
|
|
138
167
|
)
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
168
|
+
continue
|
|
169
|
+
|
|
170
|
+
last_exc: BaseException | None = None
|
|
171
|
+
for attempt in range(_MAX_ATTEMPTS):
|
|
172
|
+
try:
|
|
173
|
+
await table.optimize()
|
|
174
|
+
last_exc = None
|
|
175
|
+
break
|
|
176
|
+
except Exception as exc:
|
|
177
|
+
last_exc = exc
|
|
178
|
+
if _is_retryable(exc) and attempt < _MAX_ATTEMPTS - 1:
|
|
179
|
+
await asyncio.sleep(_BASE_BACKOFF_S * (2**attempt))
|
|
180
|
+
continue
|
|
181
|
+
# Non-retryable, or retries exhausted: stop the loop and
|
|
182
|
+
# surface below — do not swallow silently.
|
|
183
|
+
break
|
|
184
|
+
|
|
185
|
+
if last_exc is None:
|
|
186
|
+
results[name] = "ok"
|
|
187
|
+
if not quiet:
|
|
188
|
+
print(
|
|
189
|
+
f"java-codebase-rag: optimize: {name} ok",
|
|
190
|
+
file=sys.stderr,
|
|
191
|
+
)
|
|
192
|
+
else:
|
|
193
|
+
results[name] = f"error: {last_exc}"
|
|
194
|
+
failed = True
|
|
195
|
+
print(
|
|
196
|
+
f"java-codebase-rag: optimize: {name} failed: {last_exc}",
|
|
197
|
+
file=sys.stderr,
|
|
198
|
+
)
|
|
199
|
+
finally:
|
|
200
|
+
# ``AsyncConnection.close`` is a *sync* method in lancedb 0.30.x.
|
|
201
|
+
db.close()
|
|
202
|
+
return results
|
|
203
|
+
except Exception:
|
|
204
|
+
# An unexpected exception (e.g. ``connect_async`` raised, or a table-
|
|
205
|
+
# independent failure) must still flip the terminal event to failed so
|
|
206
|
+
# the renderer's task doesn't render a green check on a crash. Re-raise
|
|
207
|
+
# after marking — the caller (``_maybe_run_serialized_optimize`` /
|
|
208
|
+
# ``run_refresh_pipeline``) treats optimize failure as non-fatal and
|
|
209
|
+
# logs it, but the renderer must reflect the truth.
|
|
210
|
+
failed = True
|
|
211
|
+
raise
|
|
145
212
|
finally:
|
|
146
|
-
#
|
|
147
|
-
|
|
148
|
-
|
|
213
|
+
# Always emit a terminal optimize event so the renderer's task never
|
|
214
|
+
# hangs at "running" — even on exception (the parent treats a failed
|
|
215
|
+
# optimize as non-fatal: the index is still searchable un-compacted).
|
|
216
|
+
if on_progress is not None:
|
|
217
|
+
on_progress(
|
|
218
|
+
_make_optimize_event(
|
|
219
|
+
status="failed" if failed else "done",
|
|
220
|
+
elapsed_s=time.perf_counter() - t0,
|
|
221
|
+
)
|
|
222
|
+
)
|