Coverage for src / lilbee / app / reset.py: 100%
36 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-05-15 20:55 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-05-15 20:55 +0000
1"""Knowledge base reset (delete all documents and data)."""
3from __future__ import annotations
5import logging
6import shutil
7from pathlib import Path
9from pydantic import BaseModel
11from lilbee.core.config import cfg
12from lilbee.core.security import validate_path_within
15class ResetResult(BaseModel):
16 """Result of a full knowledge base reset."""
18 command: str = "reset"
19 deleted_docs: int
20 deleted_data: int
21 skipped: list[str] = []
22 documents_dir: str
23 data_dir: str
26def _clear_dir(base_dir: Path, skipped: list[str]) -> int:
27 """Delete all items in *base_dir*, appending undeletable paths to *skipped*."""
28 log = logging.getLogger(__name__)
29 deleted = 0
30 if not base_dir.exists():
31 return deleted
32 for item in list(base_dir.iterdir()):
33 validate_path_within(item, base_dir)
34 try:
35 if item.is_dir():
36 shutil.rmtree(item)
37 else:
38 item.unlink()
39 except OSError as exc:
40 # best-effort: reset is "delete as much as you can", and the
41 # caller surfaces the skipped list to the user verbatim.
42 log.warning("Could not delete %s: %s", item, exc)
43 skipped.append(str(item))
44 continue
45 deleted += 1
46 return deleted
49def perform_reset() -> ResetResult:
50 """Delete all documents and data. Returns summary of what was deleted."""
51 skipped: list[str] = []
52 deleted_docs = _clear_dir(cfg.documents_dir, skipped)
53 deleted_data = _clear_dir(cfg.data_dir, skipped)
55 return ResetResult(
56 deleted_docs=deleted_docs,
57 deleted_data=deleted_data,
58 skipped=skipped,
59 documents_dir=str(cfg.documents_dir),
60 data_dir=str(cfg.data_dir),
61 )