feat(maria): accepta .xml in depozit, preferat peste .md, cu chunking pe probleme

Dosarul document_store din Drive are 3 surse .xml pe care depozitul le ignora
complet, fiindca store.py accepta doar .txt/.md. La d406_saft_knowledge exista
ambele formate, iar .xml e cu trei luni mai nou (2026-01-28 vs 2025-10-15) si cu
50% mai mare (64 KB vs 41 KB) — deci indexam varianta mai saraca.

- store.py devine sursa unica pentru extensii (DOC_EXTENSIONS = .txt/.md/.xml).
  Cand acelasi nume de baza exista in mai multe formate, la indexare intra unul
  singur, cel mai bogat (.xml > .md > .txt); celalalt ramane pe disc, marcat
  `shadowed_by`. Fara asta, acelasi raspuns ar aparea de doua ori in rezultate.
  `list_documents()` arata tot (dashboard), `documents_for_index()` doar
  castigatorii (indexer).
- indexer.py taie XML-ul altfel: un chunk per element de nivel 1, adica o
  problema = un chunk, cu <mesaj_eroare> si <rezolvare> impreuna. Taierea pe
  linii goale le-ar separa si cautarea ar returna eroarea fara raspuns.
  Etichetele raman prefixe lizibile ("mesaj eroare: ..."), fara paranteze
  unghiulare care doar dilueaza embedding-ul. XML invalid nu opreste indexarea:
  cade pe taierea obisnuita, cu o linie in log. Elementele peste 4000 de
  caractere se taie mai departe pe granite de cuvant — `chunk_text` imparte doar
  pe linii goale, deci un element scris ca un paragraf lung ar fi ramas intreg
  (prins de test).
- sync.py: amprenta si `rclone --include` derivate din DOC_EXTENSIONS.
- dashboard: acelasi filtru si aceeasi preferinta (copie, fiindca nu poate
  importa `store` — coliziune de nume pe `config`), plus marcajul "umbrit de X"
  in tabelul de documente si numarul de documente chiar indexate.
- README: sectiunea Drive rescrisa pe `rclone authorize` (autorizezi pe o masina
  cu browser, muti tokenul) in loc de cont de serviciu — mai putini pasi, fara
  consola Google Cloud. Documentat si ca `sync` sterge local ce nu mai e in Drive.

tests/ nou (20 de teste, fara retea si fara Ollama): preferinta de format,
vizibilitatea in dashboard, taierea XML, entitati, comentarii, XML invalid,
elemente uriase. Suita puntii Discord: 426 pass, neafectata.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
This commit is contained in:
Claude Agent
2026-08-31 18:15:03 +00:00
parent c42b95b7db
commit 1c7899753a
11 changed files with 400 additions and 61 deletions

View File

@@ -22,6 +22,7 @@ import time
import config
import indexer
import store
STATE_FILE_NAME = ".sync_state.json"
@@ -32,7 +33,7 @@ def _fingerprint() -> str:
config.DOCS_DIR.mkdir(parents=True, exist_ok=True)
h = hashlib.sha256()
for f in sorted(config.DOCS_DIR.glob("*")):
if f.is_file() and f.suffix in (".txt", ".md"):
if f.is_file() and f.suffix in store.DOC_EXTENSIONS:
st = f.stat()
h.update(f"{f.name}:{st.st_mtime_ns}:{st.st_size}\n".encode())
return h.hexdigest()
@@ -65,8 +66,8 @@ def pull_from_drive() -> dict:
config.DOCS_DIR.mkdir(parents=True, exist_ok=True)
try:
r = subprocess.run(
["rclone", "sync", remote, str(config.DOCS_DIR),
"--include", "*.txt", "--include", "*.md"],
["rclone", "sync", remote, str(config.DOCS_DIR)]
+ [arg for ext in store.DOC_EXTENSIONS for arg in ("--include", f"*{ext}")],
capture_output=True, text=True, timeout=300,
)
except FileNotFoundError: