tensor-grep 1.63.0__tar.gz → 1.63.2__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.

Potentially problematic release.


This version of tensor-grep might be problematic. Click here for more details.

Files changed (131) hide show
  1. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/PKG-INFO +1 -1
  2. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/pyproject.toml +3 -1
  3. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/Cargo.lock +1 -1
  4. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/Cargo.toml +3 -1
  5. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/cli/main.py +41 -0
  6. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/core/reranker.py +49 -18
  7. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/LICENSE +0 -0
  8. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/NOTICE +0 -0
  9. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/README.md +0 -0
  10. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/deny.toml +0 -0
  11. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/rust-toolchain.toml +0 -0
  12. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/src/backend_ast.rs +0 -0
  13. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/src/backend_ast_workflow.rs +0 -0
  14. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/src/backend_cpu.rs +0 -0
  15. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/src/bin/tg_search_fast.rs +0 -0
  16. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/src/cli.rs +0 -0
  17. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/src/crossover.rs +0 -0
  18. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/src/editor_plane.rs +0 -0
  19. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/src/gpu_native.rs +0 -0
  20. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/src/index.rs +0 -0
  21. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/src/lib.rs +0 -0
  22. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/src/main.rs +0 -0
  23. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/src/mmap_arrow.rs +0 -0
  24. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/src/native_search.rs +0 -0
  25. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/src/python_sidecar.rs +0 -0
  26. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/src/rg_passthrough.rs +0 -0
  27. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/src/routing.rs +0 -0
  28. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/src/runtime_paths.rs +0 -0
  29. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/tests/test_ast_backend.rs +0 -0
  30. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/tests/test_ast_cache.rs +0 -0
  31. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/tests/test_ast_grep_embed.rs +0 -0
  32. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/tests/test_ast_rewrite.rs +0 -0
  33. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/tests/test_audit_manifest_symlink_unix.rs +0 -0
  34. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/tests/test_gpu_native.rs +0 -0
  35. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/tests/test_gpu_native_error_handling.rs +0 -0
  36. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/tests/test_gpu_native_graphs.rs +0 -0
  37. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/tests/test_gpu_native_integration.rs +0 -0
  38. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/tests/test_gpu_native_long_lines.rs +0 -0
  39. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/tests/test_gpu_native_streams.rs +0 -0
  40. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/tests/test_index.rs +0 -0
  41. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/tests/test_mmap_arrow.rs +0 -0
  42. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/tests/test_native_search.rs +0 -0
  43. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/tests/test_output.rs +0 -0
  44. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/tests/test_public_native_cli_parity.rs +0 -0
  45. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/tests/test_python_bootstrap.rs +0 -0
  46. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/tests/test_release_profile.rs +0 -0
  47. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/tests/test_replace.rs +0 -0
  48. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/tests/test_resident_worker.rs +0 -0
  49. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/tests/test_routing.rs +0 -0
  50. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/tests/test_runtime_path_resolution.rs +0 -0
  51. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/tests/test_schema_compat.rs +0 -0
  52. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/tests/test_search.rs +0 -0
  53. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/tests/test_search_golden.rs +0 -0
  54. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/tests/test_sidecar_ipc.rs +0 -0
  55. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/rust_core/tests/test_smart_routing.rs +0 -0
  56. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/__init__.py +0 -0
  57. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/__main__.py +0 -0
  58. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/backends/__init__.py +0 -0
  59. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/backends/ast_backend.py +0 -0
  60. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/backends/ast_wrapper_backend.py +0 -0
  61. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/backends/base.py +0 -0
  62. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/backends/cpu_backend.py +0 -0
  63. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/backends/cudf_backend.py +0 -0
  64. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/backends/cybert_backend.py +0 -0
  65. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/backends/ripgrep_backend.py +0 -0
  66. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/backends/rust_backend.py +0 -0
  67. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/backends/stringzilla_backend.py +0 -0
  68. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/backends/torch_backend.py +0 -0
  69. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/cli/__init__.py +0 -0
  70. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/cli/_index_lock.py +0 -0
  71. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/cli/agent_capsule.py +0 -0
  72. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/cli/apply_policy.py +0 -0
  73. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/cli/ast_workflows.py +0 -0
  74. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/cli/audit_manifest.py +0 -0
  75. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/cli/bootstrap.py +0 -0
  76. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/cli/checkpoint_store.py +0 -0
  77. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/cli/commands.py +0 -0
  78. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/cli/docs_coverage.py +0 -0
  79. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/cli/dogfood.py +0 -0
  80. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/cli/evidence_receipt.py +0 -0
  81. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/cli/formatters/__init__.py +0 -0
  82. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/cli/formatters/base.py +0 -0
  83. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/cli/formatters/csv_fmt.py +0 -0
  84. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/cli/formatters/json_fmt.py +0 -0
  85. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/cli/formatters/ripgrep_fmt.py +0 -0
  86. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/cli/formatters/table_fmt.py +0 -0
  87. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/cli/inventory.py +0 -0
  88. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/cli/lang_go.py +0 -0
  89. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/cli/lang_registry.py +0 -0
  90. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/cli/lsp_external_provider.py +0 -0
  91. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/cli/lsp_provider_setup.py +0 -0
  92. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/cli/lsp_server.py +0 -0
  93. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/cli/mcp_server.py +0 -0
  94. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/cli/orient_capsule.py +0 -0
  95. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/cli/progress.py +0 -0
  96. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/cli/repo_map.py +0 -0
  97. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/cli/rg_contract.py +0 -0
  98. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/cli/rule_packs.py +0 -0
  99. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/cli/runtime_paths.py +0 -0
  100. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/cli/scan_guardrails.py +0 -0
  101. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/cli/session_daemon.py +0 -0
  102. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/cli/session_store.py +0 -0
  103. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/cli/subprocess_policy.py +0 -0
  104. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/core/__init__.py +0 -0
  105. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/core/config.py +0 -0
  106. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/core/hardware/__init__.py +0 -0
  107. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/core/hardware/device_detect.py +0 -0
  108. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/core/hardware/device_inventory.py +0 -0
  109. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/core/hardware/memory_manager.py +0 -0
  110. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/core/observability.py +0 -0
  111. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/core/pipeline.py +0 -0
  112. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/core/query_analyzer.py +0 -0
  113. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/core/registration_check.py +0 -0
  114. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/core/result.py +0 -0
  115. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/core/retrieval_bm25.py +0 -0
  116. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/core/retrieval_chunker.py +0 -0
  117. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/core/retrieval_dense.py +0 -0
  118. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/core/retrieval_fusion.py +0 -0
  119. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/core/retrieval_late.py +0 -0
  120. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/core/retrieval_lexical.py +0 -0
  121. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/core/retrieval_scoring.py +0 -0
  122. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/core/semantic_index.py +0 -0
  123. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/io/__init__.py +0 -0
  124. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/io/base.py +0 -0
  125. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/io/directory_scanner.py +0 -0
  126. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/io/reader_cudf.py +0 -0
  127. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/io/reader_dstorage.py +0 -0
  128. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/io/reader_fallback.py +0 -0
  129. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/io/reader_kvikio.py +0 -0
  130. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/perf_guard.py +0 -0
  131. {tensor_grep-1.63.0 → tensor_grep-1.63.2}/src/tensor_grep/sidecar.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tensor-grep
3
- Version: 1.63.0
3
+ Version: 1.63.2
4
4
  Requires-Dist: typer>=0.12,<0.26
5
5
  Requires-Dist: rich>=13.0
6
6
  Requires-Dist: mcp>=1.2.0
@@ -480,12 +480,14 @@ version_variables = [
480
480
 
481
481
 
482
482
 
483
+
484
+
483
485
 
484
486
 
485
487
 
486
488
  [project]
487
489
  name = "tensor-grep"
488
- version = "1.63.0"
490
+ version = "1.63.2"
489
491
  description = "High-performance grep-compatible CLI with Rust, GPU, and AST/NLP routing"
490
492
  readme = { file = "rust_core/README.md", content-type = "text/markdown" }
491
493
  requires-python = ">=3.11"
@@ -1579,7 +1579,7 @@ dependencies = [
1579
1579
 
1580
1580
  [[package]]
1581
1581
  name = "tensor_grep_rs"
1582
- version = "1.63.0"
1582
+ version = "1.63.2"
1583
1583
  dependencies = [
1584
1584
  "aho-corasick",
1585
1585
  "anyhow",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "tensor_grep_rs"
3
- version = "1.63.0"
3
+ version = "1.63.2"
4
4
  edition = "2021"
5
5
  description = "Rust core engine for tensor-grep"
6
6
  license = "Apache-2.0"
@@ -388,6 +388,8 @@ path = "src/bin/tg_search_fast.rs"
388
388
 
389
389
 
390
390
 
391
+
392
+
391
393
 
392
394
 
393
395
 
@@ -22,7 +22,9 @@ from uuid import uuid4
22
22
  if sys.platform.startswith("win") and not sys.stdout.isatty():
23
23
  os.environ.setdefault("TYPER_USE_RICH", "0")
24
24
 
25
+ import click
25
26
  import typer
27
+ from typer.core import TyperGroup
26
28
 
27
29
  from tensor_grep.backends.base import BackendExecutionError
28
30
  from tensor_grep.cli import ast_workflows
@@ -230,7 +232,46 @@ review_bundle_app = typer.Typer(
230
232
  help="Create and verify enterprise review bundles.",
231
233
  no_args_is_help=True,
232
234
  )
235
+
236
+
237
+ class _EvidenceGroup(TyperGroup):
238
+ """Nudge `tg evidence <path>` toward the `emit` subcommand.
239
+
240
+ Dogfood trap (v1.61.2): an agent reaches for `tg evidence <PATH> <query>` by
241
+ analogy with `tg defs`/`tg orient` (which take a path directly), but `evidence`
242
+ is a command GROUP whose only action is `emit`. Click's default
243
+ "No such command 'src/...'" is correct (exit 2) but unhelpful -- when the unknown
244
+ subcommand looks like a filesystem path, append the concrete fix so the caller
245
+ does not have to re-read `--help`.
246
+ """
247
+
248
+ def resolve_command(
249
+ self, ctx: click.Context, args: list[str]
250
+ ) -> tuple[str | None, click.Command | None, list[str]]:
251
+ try:
252
+ return super().resolve_command(ctx, args)
253
+ except click.exceptions.UsageError as exc:
254
+ token = args[0] if args else ""
255
+ if (
256
+ token
257
+ and not token.startswith("-")
258
+ and ("/" in token or "\\" in token or Path(token).exists())
259
+ ):
260
+ # Re-raise a NEW UsageError with the hint appended -- `UsageError.message` is a
261
+ # Final attribute (cannot be reassigned in place); a fresh error carrying the same
262
+ # `ctx` renders identically (Usage + "Try --help" + Error:) and keeps exit code 2.
263
+ raise click.exceptions.UsageError(
264
+ f"{exc.format_message()}\n"
265
+ "Hint: `tg evidence` is a command group; its receipt action is "
266
+ f"`emit`. Did you mean `tg evidence emit {token}`? "
267
+ "(run `tg evidence emit --help`)",
268
+ ctx=exc.ctx,
269
+ ) from exc
270
+ raise
271
+
272
+
233
273
  evidence_app = typer.Typer(
274
+ cls=_EvidenceGroup,
234
275
  help="Emit a versioned EvidenceReceipt aggregating tg's existing outputs.",
235
276
  no_args_is_help=True,
236
277
  )
@@ -13,7 +13,7 @@ from __future__ import annotations
13
13
  import dataclasses
14
14
  import os
15
15
  import sys
16
- import time
16
+ import threading
17
17
  from collections import defaultdict
18
18
 
19
19
  from tensor_grep.core.result import SearchResult
@@ -212,26 +212,57 @@ def rerank_hybrid(
212
212
  )
213
213
  budget_ms = _int_env(_RERANK_BUDGET_MS_ENV, _DEFAULT_RERANK_BUDGET_MS)
214
214
  head = fused_order[:pool_k]
215
- late_rerank_start = time.perf_counter()
216
- try:
217
- reranked_head = late_reranker.rerank(query, [chunks[i].text for i in head], head)
218
- except LateRerankUnavailableError as exc:
219
- # RECOVERABLE (e.g. a malformed embedding shape from the injected encoder): degrade
220
- # to the plain RRF order for this pool, never crash. A genuine BackendExecutionError
221
- # (a real encode-time fault) is deliberately NOT caught here -- it propagates to the
222
- # CLI boundary (cli/main.py ~:6804-6813) per the Backend Fail-Closed Contract.
223
- late_rank_fallback_reason = str(exc)
224
- sys.stderr.write(f"tg: {late_rank_fallback_reason}\n")
225
- else:
226
- elapsed_ms = (time.perf_counter() - late_rerank_start) * 1000.0
227
- if elapsed_ms > budget_ms:
228
- late_rank_fallback_reason = (
229
- "late rerank unavailable: budget exceeded "
230
- f"({elapsed_ms:.0f}ms > {budget_ms}ms at pool_k={pool_k})"
215
+ # Real wall-clock deadline (A3, external audit 2026-07-11): the previous post-hoc
216
+ # `elapsed > budget` check could only DISCARD a rerank that had already RETURNED -- a HUNG
217
+ # encoder (a wedged model, an infinite loop in the injected reranker) never returns, so
218
+ # `late_reranker.rerank` blocked `tg search --rank` indefinitely with no bound. Run it on a
219
+ # daemon thread and `join` on the budget: if it overruns, abandon the thread (it dies with
220
+ # this short-lived CLI process) and degrade. The Backend Fail-Closed Contract is PRESERVED
221
+ # across the thread boundary -- a LateRerankUnavailableError (recoverable) degrades to the
222
+ # plain RRF order; ANYTHING ELSE (a genuine BackendExecutionError encode fault, or a
223
+ # KeyboardInterrupt/SystemExit user-abort) is re-raised on this thread so it still
224
+ # propagates to the CLI boundary (cli/main.py ~:6804-6813), exactly as the synchronous
225
+ # version did. Capturing BaseException (not just Exception) is deliberate: it keeps a
226
+ # user-abort from being silently swallowed into an RRF degrade AND guarantees exactly one
227
+ # of the two result holders is populated when the worker finishes (so the `else` splice
228
+ # below can never IndexError on an empty result).
229
+ rerank_result: list[list[int]] = []
230
+ rerank_error: list[BaseException] = []
231
+
232
+ def _run_late_rerank() -> None:
233
+ try:
234
+ rerank_result.append(
235
+ late_reranker.rerank(query, [chunks[i].text for i in head], head)
231
236
  )
237
+ except (
238
+ BaseException
239
+ ) as exc: # classified + re-raised (if non-recoverable) by the caller
240
+ rerank_error.append(exc)
241
+
242
+ worker = threading.Thread(target=_run_late_rerank, name="tg-late-rerank", daemon=True)
243
+ worker.start()
244
+ worker.join(timeout=budget_ms / 1000.0)
245
+
246
+ if worker.is_alive():
247
+ # Still running at the deadline (hung or merely too slow): do NOT wait -- abandon the
248
+ # daemon thread and degrade. This is the only branch that bounds a genuinely HUNG
249
+ # encoder; the old post-hoc check could never run for one.
250
+ late_rank_fallback_reason = (
251
+ f"late rerank unavailable: budget exceeded (>{budget_ms}ms at pool_k={pool_k})"
252
+ )
253
+ sys.stderr.write(f"tg: {late_rank_fallback_reason}\n")
254
+ elif rerank_error:
255
+ exc = rerank_error[0]
256
+ if isinstance(exc, LateRerankUnavailableError):
257
+ # RECOVERABLE (e.g. a malformed embedding shape): degrade, never crash.
258
+ late_rank_fallback_reason = str(exc)
232
259
  sys.stderr.write(f"tg: {late_rank_fallback_reason}\n")
233
260
  else:
234
- fused_order = reranked_head + fused_order[pool_k:]
261
+ # A genuine encode-time fault -> propagate (Backend Fail-Closed Contract): never
262
+ # silently degrade a real error into a plausible-but-wrong ranking.
263
+ raise exc
264
+ else:
265
+ fused_order = rerank_result[0] + fused_order[pool_k:]
235
266
 
236
267
  # Position in the fused order is a monotonic proxy for the underlying RRF score: RRF ties are
237
268
  # already broken by ascending chunk index before this list is built, so using position
File without changes
File without changes