CV3 / app.py
firmanaziz's picture
Update app.py
9b39287 verified
# ============================================================
# πŸ’Ό 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()