# ============================================================ # 💼 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": , "penilaian_per_kategori": {{ "kelengkapan_informasi": , "keterbacaan_dan_format": , "dampak_pengalaman_kerja": }}, "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()