# 1. Judul
**Blueprint Modularisasi: Pemisahan LLM Extractor dan Pydantic Schema dari Main Orchestrator (V3)**

# 2. Tanggal
Senin, 11 Mei 2026

# 3. Deskripsi
Dokumen ini merupakan cetak biru lanjutan (*blueprint*) untuk mengeksekusi "Modularisasi Kode" pada `app/services/ingestion.py`. Fokus utamanya adalah memindahkan seluruh logika ekstraksi Pydantic (Model Data) dan _System Prompt_ (Instruksi LLM) ke dalam *file* eksternal yang mandiri, yaitu `app/services/parser/llm_extractor.py`. Tujuan akhir dari tugas ini adalah menerapkan prinsip *Separation of Concerns* (SoC), di mana `ingestion.py` murni berfungsi sebagai *Orchestrator* aliran data, sedangkan `llm_extractor.py` khusus menjadi otak (*Engine*) ekstraksi bahasa.

# 4. Metodologi
Proses migrasi modular ini mengadopsi pola *Service Injection*:
1.  **Code Relocation:** Memindahkan semua fungsionalitas pembersihan teks (Lapis 1 & 3), model data bersarang Pydantic (`LiteratureMetadata`, `LiteraturePillars`, `ExtractionResponse`), dan fungsi `extract_pillars_with_llm` ke berkas `llm_extractor.py`.
2.  **Dependency Injection (Semaphore Mitigation):** Saat ini, fungsi `extract_pillars_with_llm` bergantung pada objek global `api_semaphore` (yang berada di `ingestion.py`) untuk menahan *Rate Limit* saat pemanggilan DashScope. Agar _file_ baru ini tidak bergantung kembali secara melingkar (_circular import_) ke `ingestion.py`, variabel `api_semaphore` akan disuntikkan (*injected*) sebagai argumen parameter dari luar, atau modul konfigurasi semafor akan dipusatkan.
3.  **Namespace Resolution:** Memastikan `import json, re` dan _decorator_ `@retry` dari `tenacity` ikut diboyong ke rumah barunya.

# 5. Keputusan (Decision Log) & Blind Spot Evaluation
1.  **Blind Spot 1 - Variabel `api_semaphore` & `DASHSCOPE_MODEL`:** Pemindahan fungsi utama berarti fungsi tersebut akan kehilangan referensi ke dua variabel global ini. **Keputusan:** Fungsi `extract_pillars_with_llm` akan menerima `model_name` dan `semaphore` sebagai parameter operasional yang dilempar dari `ingestion.py`.
2.  **Blind Spot 2 - Duplikasi Import:** Modul `pydantic` dan `tenacity` harus disertakan di `llm_extractor.py`, sementara `ingestion.py` tetap mempertahankan beberapa untuk fungsi lain (jika ada). **Keputusan:** Pembersihan _import header_ di kedua _file_ secara teliti.
3.  **Arsitektur Kode Akhir:** File `ingestion.py` akan dipangkas sekitar 150+ baris kode, menjadikannya jauh lebih ringkas, *readable*, dan khusus fokus pada logika *Graph Database* (Neo4j) dan penjadwalan antrean (`queue`).

# 6. Alur Diagram
```mermaid
flowchart LR
    classDef main fill:#e3f2fd,stroke:#1565c0,stroke-width:2px;
    classDef module fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px;
    classDef lib fill:#fff3e0,stroke:#f57c00,stroke-width:2px;

    subgraph Orchestrator ["ingestion.py (Main)"]
        A[File PDF/Markdown]:::main
        B{ThreadPoolExecutor}:::main
        C[api_semaphore]:::main
        D[Neo4j Driver]:::main
    end

    subgraph Service ["llm_extractor.py (Modul Baru)"]
        E[extract_pillars_with_llm]:::module
        F[ExtractionResponse / Pydantic]:::module
        G[clean_llm_json_string]:::module
    end

    A --> B
    B --> |"text, client, semaphore"| E
    C -.-> |"Inject Dependency"| E
    E --> F
    G -.-> F
    F --> |"Return Parsed Dict"| D
```

# 7. Struktur Folder (Tree)
Pembaruan ini menargetkan penciptaan dan modifikasi pada:
```text
/home/daegir/Documents/PERCOBAAN/backend-sre-(Knowledge Extractor)/
├── app/
│   └── services/
│       ├── ingestion.py                 # [TARGET 1: Dipangkas, Hapus Pydantic & Prompt]
│       └── parser/
│           └── llm_extractor.py         # [TARGET 2: Dibuat/Ditulis ulang, Menampung Pydantic & Prompt]
└── Markdown/
    └── prompt-modularization-v3/        
        ├── Plan/
        │   └── modularization-plan-v3.md   # File dokumen ini
        └── Log-Execution/               # Untuk pelaporan eksekusi nanti
```

# 8. Asumsi & Batasan
*   **Asumsi:** Fungsi `generate_edge_reasoning` yang juga menggunakan `api_semaphore` dan LLM untuk sementara **dibiarkan** di `ingestion.py` karena _task_ utamanya sangat melekat erat dengan evaluasi kemiripan _node_ (Neo4j Edge Generation). Modularisasi saat ini **hanya** difokuskan pada `extract_pillars_with_llm` (pembuat struktur utama jurnal).
*   **Batasan:** Tidak ada perubahan logika *prompting* yang telah sukses dieksekusi di fase V2. Pemindahan murni bersifat *Cut and Paste* dengan sedikit modifikasi parameter fungsi.

# 9. Konsep Tabel Metriks
Keberhasilan pemisahan arsitektur ini akan diukur melalui:
| Metrik | Parameter Evaluasi | Status Target |
| :--- | :--- | :--- |
| **Code Readability** | Total baris kode (LoC) di `ingestion.py` | Menurun drastis (-150 baris) |
| **Dependency Decoupling** | Tidak ada `api_semaphore` yang _hardcoded_ di fungsi ekstraksi | Menggunakan _parameter injection_ |
| **Pipeline Stability** | `python app/services/ingestion.py` | Sukses mem- _parsing_ PDF tanpa _ImportError_ |

# 10. Langkah Implementasi
1.  **Pembuatan File Modul:** Memastikan file `app/services/parser/llm_extractor.py` eksis dan kosong (jika ada file lama dari _PoC_, akan di-*overwrite*).
2.  **Pemindahan _Helper Functions_:** Memotong `clean_llm_json_string` dan `sanitize_llm_field` dari `ingestion.py` ke `llm_extractor.py`.
3.  **Pemindahan _Pydantic Models_:** Memotong `LiteratureMetadata`, `LiteraturePillars`, dan `ExtractionResponse` ke `llm_extractor.py`.
4.  **Pemindahan Fungsi Utama:** Memotong `extract_pillars_with_llm` beserta variabel _Prompt_-nya ke `llm_extractor.py`.
5.  **Perbaikan _Signature_ Fungsi:** Mengubah _signature_ fungsi ekstraksi agar bisa menerima argumen `model_name` dan `semaphore` (sehingga tidak bergantung pada variabel global).
    ```python
    def extract_pillars_with_llm(text: str, client, model_name: str, semaphore) -> tuple[dict, dict]:
        with semaphore:
            ...
    ```
6.  **Penyisipan Header Impor:** Menambahkan semua `import` yang dibutuhkan oleh Pydantic, JSON, dan Tenacity di `llm_extractor.py`.
7.  **Pembaruan Pemanggil (*Caller*):** Di `ingestion.py`, menambahkan `from app.services.parser.llm_extractor import extract_pillars_with_llm`, dan menyesuaikan argumen pemanggilannya.
8.  **Uji Coba _Dry-Run_:** Menjalankan ulang pipeline.

# 11. Tabel Eksekusi Task

| Check Box | Fase | Task | Detail Task | Deskripsi | Acceptance Criteria |
| :---: | :---: | :---: | :--- | :--- | :--- |
| [x] | 1 - Isolation | Buat/Overwrite `llm_extractor.py` | Pindahkan fungsi _sweeper_ dan model Pydantic. | Mengisi file modul dengan fungsi `clean_llm_json_string`, `sanitize_llm_field`, dan struktur `ExtractionResponse`. | File modul valid dan memiliki semua deklarasi *import* (Pydantic, dll) tanpa _error_. |
| [x] | 2 - Decoupling | Migrasi `extract_pillars_with_llm` | Pindahkan logika LLM dan ubah *signature* fungsi. | Memindahkan instruksi _Prompt_ multi-bahasa, menyuntikkan `semaphore` dan `model_name` ke param fungsi. | Fungsi di dalam modul tidak mendeteksi variabel _undefined_ untuk semafor dan nama model. |
| [x] | 3 - Integration | Refactor `ingestion.py` | Hapus kode sisa dan impor fungsi baru. | Membersihkan Pydantic dan prompt dari `ingestion.py`, lalu memanggil modul baru pada blok eksekusi _I/O Bound_. | File `ingestion.py` bersih, _import statement_ tepat, dan pemanggilan argumen tidak memicu _TypeError_. |
| [x] | 4 - Testing | Dry-Run Pipeline Baru | Jalankan dengan `pdf_files[:1]`. | Menjalankan seluruh sistem _end-to-end_ untuk membuktikan tidak ada disonansi _import_ (_circular_). | Skrip selesai dieksekusi dengan status _Success_, modul terpisah terbukti mengeksekusi LLM dengan lancar. |
| [x] | 5 - Reporting | Dokumentasi Migrasi V3 | Buat laporan _Log-Execution_. | Mencatat keberhasilan _refactoring_ arsitektur modul LLM. | File *log* tersedia di `Markdown/prompt-modularization-v3/Log-Execution/`. |
