Đọc TRƯỚC khi đụng vào code. Vi phạm các quy tắc đánh dấu MUST sẽ làm hỏng hiệu năng hoặc độ chính xác.
- YAGNI → KISS → DRY theo thứ tự. Không code phỏng đoán, không trừu tượng dùng một lần.
- Thay đổi phẫu thuật: chỉ đụng những gì cần. Không "tốt lên" code/sidebar/format kề cạnh.
- Hành vi thật, không mock/fake dữ liệu để qua cổng kiểm tra.
- Commit theo conventional format, không nhắc AI trong commit/code.
| Crate | Pin | Lý do |
|---|---|---|
pdf-extract |
=0.8.2 |
0.12 panic trên một số PDF mà 0.8.2 xử lý được |
symphonia |
0.5 |
0.6 cấu trúc lại module, đổi API |
time (app) |
=0.3.51 |
tương thích cookie 0.18.1 |
sha2 (workspace, sha2 = "=0.11.0") |
=0.11.0 |
contract durable intelligence ID sha256-v1 (ADR 0013): DefaultHasher không ổn định qua version Rust nên ID SQLite/HNSW/handoff sẽ trôi; pin để cùng họ crate với ADR 0006 (server digest) — đừng nâng ngoài quy trình ADR |
Crate chính (không pin cứng nhưng cố ý giữ ổn định): pdfium-render 0.9.2, pdf-inspector 0.1.3,
docx-rust 0.1.11, whisper-rs 0.16, htmd 0.5, calamine 0.35, quick-xml 0.37, zip 2.2,
csv 1.3, image 0.25, unicode-normalization 0.1.25.
PDF và whisper đắt → phải giữ pattern cache. Đừng "dọn" thành gọi thẳng mỗi lần.
- PDFium thread_local + PDFIUM_CALL lock (
crates/core/src/conv/pdf/pdfium.rs):thread_local! { static PDFIUM: Option<Pdfium> = load_pdfium() }. Mỗi thread 1 instance, init 1 lần/tiến trình. Chỉ load khi thực sự cần OCR (need_pdfiumgate). libpdfium KHÔNG thread-safe: mỗi region dùng PDFium (cả render+OCR) phải acquirePDFIUM_CALL: Mutex<()>trước. Concurrent scanned-PDF conversions sẽ queue tại lock (trade-off vs throughput). Đường dẫn lib qua envFILECONV_PDFIUM_LIB→pdfium/lib/*→ thư viện hệ thống. - Whisper process-wide LRU cache (
crates/core/src/audio.rs):LoadOnceCachekeyed byWhisperModelKey(canonical model path + immutable load knobs:use_gpu/flash_attn/gpu_device). Per-key state isLoading | Ready | Failedwith a condvar (fail/retry never overlaps loaders). Ready set is LRU-bounded (default 2, overrideFILECONV_WHISPER_CACHE_CAPACITY); eviction drops the cache entry while outstandingAudioEngineArcs keep in-flight contexts alive. Production loader derives all behavior from the complete key (no public injectable loader). Runtime knobs (audio_threads,audio_no_speech_threshold) stay onAudioEngineand are not part of the cache key. Resample to 16 kHz usesrubatoFFT with partial/flush +output_delaytrim; returnsResult(never invents silence on failure). TrảUnsupportednếu chưa có model. - Temp file: file tạm (ảnh render, artifact) ghi qua
tempfile::NamedTempFile(exclusiveO_EXCL/tên random) — tránh path đoán được trong/tmp.
GUI app (Tauri) không nên hiển thị console window khi spawn CLI subprocess (python, LLM CLI…).
Luôn dùng crate::proc::background_command() thay vì Command::new() trực tiếp:
// ✅ Đúng
let output = crate::proc::background_command("some-cli")
.arg(&input_path)
.arg(out_path)
.output()?;
// ❌ Sai
let output = std::process::Command::new("some-cli")
.arg(&input_path)
.arg(out_path)
.output()?;background_command() tự động thêm CREATE_NO_WINDOW flag trên Windows. stdout/stderr capture không đổi.
Mọi output của convert_path phải chuẩn hoá NFC (unicode_normalization, có is_nfc_quick guard
tránh chuẩn hoá lại text đã NFC). Tài liệu VN thường dính NFD từ macOS/PDF cũ → bắt buộc để chữ đúng.
FormatKind::from_path match extension lowercase. Đừng thêm sniff magic-byte — sẽ phá contract
và cách app/CLI gom file.
Sau bất kỳ thay đổi nào ở image_ocr.rs, audio.rs, conv/pdf/: đo lại bằng CLI trên corpus
(tái tạo qua bench/*.sh):
./target/release/fileconv speed bench/corpus bench/REPORT_SPEED.md
./target/release/fileconv accuracy bench/vn_corpus/manifest.tsv bench/REPORT_ACCURACY.mdKhông đo lại = không claims "nhanh/đúng hơn". Quy tắc Fail loud: báo rõ nếu bỏ qua bước đo.
Đã exclude khỏi workspace. Nếu cần ý tưởng thì đọc, nhưng không use/path-dep/import từ đó.
- Rust: snake_case (
fn,mod), PascalCase (struct/enum), crate gốc gọi trực tiếp trongconv/*. Moduleconv::*là private — caller chỉ đi quaConverter::convert_path. - TypeScript (app): strict mode,
noUnusedLocals/Parameters. File.ts/.tsxkebab-case không bắt buộc nhưng komponent PascalCase. State qua Zustand store duy nhất (state/store.ts), không persist (Rust là nguồn sự thật). - Python (bench): snake_case. Chỉ để sinh corpus + thí nghiệm, không phải production code.
- Comment / UI string: tiếng Việt (theo convention dự án). Giải thích "tại sao", không lặp "cái gì".
default = []— core build tinh gọn, offline.- feature
audio(opt-in) →whisper-rs/symphonia/rubato, mở transcribe. TẮT mặc định: whisper.cpp phải compile C++ qua cmake (~1–2 phút), chỉ CLI/desktop/MCP cần — server/knowledge dùng lõi text-only không gánh chi phí này. cuda/metal/vulkan/hipblas/openblas/openmp→ kéo theo featureaudio(proxy sangwhisper-rsđể tăng tốc GPU/BLAS), không bật độc lập được.llm→reqwest(blocking, rustls-tls) +httpdate, mởpub mod llm.base64là dependency không điều kiện (dùng cả ngoàillm, ví dụpptx_preview.rs), không phải feature dep củallm. MCP crate luôn build vớillm.
- Build whisper-rs cần cmake + C/C++ + clang (bindgen). Lần đầu compile whisper.cpp ~1–2 phút.
- PDFium:
bash bench/download_pdfium.sh→./pdfium/lib. Thiếu → PDF tự fallback pdf-extract. - Whisper model:
bash bench/download_models.sh→./models/ggml-{tiny,base,small}.bin+ PhoWhisper-small. - OCR ảnh/scan: vision-LLM (
FILECONV_OCR_API_KEY, mặc định OpenRouter; ADR 0016 — Tesseract/Paddle local đã loại bỏ; self-host vision endpoint quaFILECONV_OCR_BASE_URL).
Override đường dẫn qua env: FILECONV_PDFIUM_LIB, FILECONV_WHISPER_MODEL.
pdf-extractvàpdf-inspectorđều bọccatch_unwind(AssertUnwindSafe)— lopdf/pdf-extract panic trên PDF malformed. Giữ wrapper.tables.rsCSV path dùngString::from_utf8_lossyvà không decode TCVN3 nhưcsv_conv.rs— bất nhất đã biết.
- Map code:
codebase-summary.md - Kiến trúc & luồng:
system-architecture.md