Improve LLM and CSV processing

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Repinoid
2026-10-23 10:09:34 +03:00
co-authored by Copilot
parent aa1d15509b
commit 49171c93f9
14 changed files with 315 additions and 92 deletions
+31 -14
View File
@@ -17,10 +17,19 @@ from . import extractor
from . import scanner
from . import replacer
from . import builder
from .names import resolve_name
log = logging.getLogger("drhider")
class LLMIncompleteError(Exception):
"""Processing cannot safely return files when one or more LLM chunks failed."""
def __init__(self, llm_errors: int):
self.llm_errors = llm_errors
super().__init__("LLM processing incomplete")
def _dedupe_file_names(
files: List[Tuple[str, bytes, str]]
) -> List[Tuple[str, bytes, str]]:
@@ -39,17 +48,9 @@ def _dedupe_file_names(
out: List[Tuple[str, bytes, str]] = []
for fname, content, ctype in files:
candidate = fname
if candidate in seen:
if seen[candidate] == content:
continue # точный дубль — пропускаем
base, ext = os.path.splitext(fname)
n = 2
while f"{base}_{n}{ext}" in seen:
n += 1
candidate = f"{base}_{n}{ext}"
seen[candidate] = content
out.append((candidate, content, ctype))
candidate = resolve_name(fname, content, seen)
if candidate is not None:
out.append((candidate, content, ctype))
return out
@@ -177,13 +178,18 @@ class TwoPassObfuscator:
cancelled_llm = cancelled_extract
if not cancelled_llm and self._llm_client:
try:
scanner.scan_llm_ner(
llm_failures = scanner.scan_llm_ner(
all_texts, self._mapping, self._llm_client, self._counters,
cancel_event=cancel_event, file_progress=_fp,
)
except scanner.CancelRequested:
except scanner.CancelRequested as e:
if e.llm_failures:
raise LLMIncompleteError(e.llm_failures)
log.info("obfuscate: LLM cancelled, llm_done=%d", len(llm_done))
cancelled_llm = True
else:
if llm_failures:
raise LLMIncompleteError(llm_failures)
# Предсортировать ключи один раз (по убыванию длины)
self._sorted_keys = sorted(
@@ -220,7 +226,18 @@ class TwoPassObfuscator:
t0 = time.time()
obf_content = content # По умолчанию — без изменений
if fname.endswith('.doc'):
if fname.lower().endswith('.csv'):
try:
obf_content = extractor.obfuscate_csv(
content, self._mapping, self._sorted_keys, self._compiled_re
)
except Exception as e:
log.warning("obfuscate: skip CSV %r during replacement: %r", fname, e)
skipped.add(fname)
if progress_cb:
progress_cb("done", i, total, display_name, 0.0)
continue
elif fname.endswith('.doc'):
# .doc — конвертируется через liberta, обрабатывается как .docx
md_text = all_texts.get(fname, '')
replaced = replacer.apply_replacements(