Spaces:
Sleeping
Sleeping
| # ============================================================ | |
| # πΌ CETHA - AI CV REVIEW (FINAL JSON VERSION with SCORE) | |
| # ============================================================ | |
| # !pip install -q gradio google-generativeai pymupdf | |
| import gradio as gr | |
| import google.generativeai as genai | |
| import fitz # PyMuPDF | |
| import json | |
| import os | |
| # ============================================================ | |
| # π KONFIGURASI API KEY | |
| # ============================================================ | |
| API_CONFIGURED = False | |
| try: | |
| api_key = os.environ.get("GEMINI_API_KEY") | |
| if api_key: | |
| genai.configure(api_key=api_key) | |
| model = genai.GenerativeModel("gemini-flash-latest") | |
| API_CONFIGURED = True | |
| print("β Konfigurasi API dan model berhasil.") | |
| else: | |
| print("π Secret 'GEMINI_API_KEY' tidak ditemukan.") | |
| except Exception as e: | |
| print(f"π Terjadi error saat inisialisasi: {e}") | |
| # ============================================================ | |
| # π§ FUNGSI PENDUKUNG | |
| # ============================================================ | |
| def ekstrak_teks_dari_pdf(path_file_pdf): | |
| """Membaca seluruh teks dari file PDF.""" | |
| try: | |
| with fitz.open(path_file_pdf) as dokumen: | |
| teks_lengkap = "".join(halaman.get_text() for halaman in dokumen) | |
| return teks_lengkap | |
| except Exception as e: | |
| raise gr.Error(f"Gagal membaca file PDF: {e}") | |
| def clean_and_parse_json(raw_text): | |
| """Membersihkan teks dari output model dan mem-parsing JSON valid.""" | |
| start_index = raw_text.find("{") | |
| end_index = raw_text.rfind("}") | |
| if start_index == -1 or end_index == -1: | |
| raise json.JSONDecodeError("Tidak ditemukan blok JSON valid dalam respons.", raw_text, 0) | |
| json_str = raw_text[start_index : end_index + 1] | |
| return json.loads(json_str) | |
| # ============================================================ | |
| # π― FUNGSI UTAMA: PENILAIAN CV DENGAN SKOR DAN DETAIL | |
| # ============================================================ | |
| def score_cv_json(cv_file): | |
| """Menilai CV dan mengembalikan hasil dalam format JSON dengan skor dan penjelasan detail.""" | |
| if not API_CONFIGURED: | |
| raise gr.Error("API Key Gemini belum terkonfigurasi. Periksa Logs aplikasi di Hugging Face.") | |
| if cv_file is None: | |
| raise gr.Error("Mohon upload file CV (PDF) Anda.") | |
| try: | |
| print("--- π Memulai Proses Penilaian CV ---") | |
| # 1οΈβ£ Ekstraksi teks dari PDF | |
| teks_cv = ekstrak_teks_dari_pdf(cv_file.name) | |
| if not teks_cv: | |
| raise gr.Error("PDF kosong atau tidak dapat dibaca.") | |
| print("β Teks berhasil diekstrak dari PDF.") | |
| # 2οΈβ£ Prompt ke Gemini untuk analisis | |
| prompt_penilaian = f""" | |
| Anda adalah seorang career coach dan perekrut profesional. | |
| Analisislah CV berikut dan berikan hasil dalam format JSON **valid**. | |
| CV Kandidat: | |
| --- | |
| {teks_cv} | |
| --- | |
| Tugas Anda: | |
| 1. Nilai CV berdasarkan 3 kategori (0β100): | |
| - "kelengkapan_informasi": Apakah CV mencakup nama, kontak, pengalaman, pendidikan, dan keterampilan? | |
| - "keterbacaan_dan_format": Apakah struktur, urutan, dan tampilan CV rapi dan mudah dibaca? | |
| - "dampak_pengalaman_kerja": Apakah deskripsi pengalaman menonjolkan hasil terukur (angka, pencapaian)? | |
| 2. Hitung "skor_keseluruhan" sebagai rata-rata dari ketiga kategori. | |
| 3. Temukan 3 KELEBIHAN utama dan 3 HAL YANG DAPAT DITINGKATKAN. | |
| Setiap poin memiliki: | |
| - "point": kalimat ringkas yang mewakili kelebihan/perbaikan. | |
| - "explanation": penjelasan singkat (1β3 kalimat) tentang konteks atau alasannya. | |
| Format output JSON yang harus dikembalikan: | |
| {{ | |
| "skor_keseluruhan": <angka>, | |
| "penilaian_per_kategori": {{ | |
| "kelengkapan_informasi": <angka>, | |
| "keterbacaan_dan_format": <angka>, | |
| "dampak_pengalaman_kerja": <angka> | |
| }}, | |
| "highlights": [ | |
| {{ | |
| "point": "judul kelebihan singkat", | |
| "explanation": "penjelasan detail" | |
| }}, | |
| ... | |
| ], | |
| "improvements": [ | |
| {{ | |
| "point": "judul perbaikan singkat", | |
| "explanation": "penjelasan detail" | |
| }}, | |
| ... | |
| ] | |
| }} | |
| Pastikan output hanya berupa JSON valid tanpa tambahan teks lain. | |
| """ | |
| print("π€ Mengirim prompt ke Gemini...") | |
| response = model.generate_content(prompt_penilaian) | |
| hasil_penilaian = clean_and_parse_json(response.text) | |
| print("β Analisis berhasil diterima dan dikonversi ke JSON.") | |
| # 3οΈβ£ Mengembalikan hasil JSON rapi | |
| return json.dumps(hasil_penilaian, indent=2, ensure_ascii=False) | |
| except Exception as e: | |
| print(f"π ERROR: {e}") | |
| raise gr.Error(f"Terjadi kesalahan: {e}") | |
| # ============================================================ | |
| # π» GRADIO INTERFACE | |
| # ============================================================ | |
| with gr.Blocks(theme=gr.themes.Soft()) as demo: | |
| gr.Markdown("# π€ CETHA - AI CV Review (JSON Output with Score)") | |
| gr.Markdown( | |
| """ | |
| Upload CV Anda dalam format **PDF** untuk mendapatkan hasil analisis otomatis berbasis AI. | |
| Sistem akan memberikan **skor keseluruhan**, **penilaian per kategori**, | |
| serta **3 poin kelebihan dan 3 poin perbaikan lengkap dengan penjelasan.** | |
| """ | |
| ) | |
| cv_pdf = gr.File(label="π Upload CV Anda (PDF)", file_types=[".pdf"]) | |
| score_button = gr.Button("π Analisis CV Sekarang", variant="primary") | |
| output_json = gr.JSON(label="π Hasil Analisis (JSON Lengkap)") | |
| score_button.click( | |
| fn=score_cv_json, | |
| inputs=[cv_pdf], | |
| outputs=[output_json], | |
| show_progress="full", | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch() | |