firmanaziz commited on
Commit
86249f9
Β·
verified Β·
1 Parent(s): 56726d1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +55 -86
app.py CHANGED
@@ -1,24 +1,7 @@
1
  import gradio as gr
2
- import google.generativeai as genai
3
  import fitz # PyMuPDF
4
  import json
5
- import os
6
  import urllib.parse
7
- import base64 # Diperlukan untuk client-side API call
8
-
9
- # --- KONFIGURASI API KEY (TETAP SAMA) ---
10
- API_CONFIGURED = False
11
- try:
12
- api_key = os.environ.get('GEMINI_API_KEY')
13
- if api_key:
14
- genai.configure(api_key=api_key)
15
- model = genai.GenerativeModel('gemini-2.5-flash')
16
- API_CONFIGURED = True
17
- print("βœ… Konfigurasi API dan model berhasil.")
18
- else:
19
- print("πŸ›‘ Secret 'GEMINI_API_KEY' tidak ditemukan.")
20
- except Exception as e:
21
- print(f"πŸ›‘ Terjadi error saat inisialisasi: {e}")
22
 
23
  # --- KONSTANTA BATAS TOKEN OUTPUT ---
24
  MAX_OUTPUT_TOKENS = 8192
@@ -47,87 +30,70 @@ def generate_search_links(keywords):
47
  }
48
  return links
49
 
50
- def parse_json_safe(text: str) -> dict:
51
- clean = text.strip()
52
- if clean.startswith("```"):
53
- parts = clean.split("```")
54
- for part in parts:
55
- candidate = part.lstrip("json").strip()
56
- if candidate.startswith("{"):
57
- clean = candidate
58
- break
59
- start = clean.find("{")
60
- end = clean.rfind("}")
61
- if start != -1 and end != -1 and end > start:
62
- clean = clean[start:end + 1]
63
- return json.loads(clean)
64
-
65
- def log_token_usage(usage_metadata):
66
- """Log penggunaan token dari usage_metadata ke console."""
67
- if usage_metadata is None:
68
- print("⚠️ Token usage: data tidak tersedia.")
69
- return
70
- prompt_tokens = getattr(usage_metadata, 'prompt_token_count', 'N/A')
71
- candidate_tokens = getattr(usage_metadata, 'candidates_token_count', 'N/A')
72
- total_tokens = getattr(usage_metadata, 'total_token_count', 'N/A')
73
- print("=" * 40)
74
- print("πŸ“Š TOKEN USAGE")
75
- print(f" πŸ”Ό Input (prompt) : {prompt_tokens}")
76
- print(f" πŸ”½ Output (response): {candidate_tokens} [limit: {MAX_OUTPUT_TOKENS}]")
77
- print(f" βž• Total : {total_tokens}")
78
- print("=" * 40)
 
 
 
 
79
 
80
  def analyze_career_path(cv_file):
81
- """Fungsi utama pipeline: Analisis CV -> Buat Laporan JSON -> Buat Link -> Gabungkan."""
82
- if not API_CONFIGURED:
83
- raise gr.Error("API Key Gemini belum terkonfigurasi. Periksa Logs aplikasi.")
84
  if cv_file is None:
85
  raise gr.Error("Mohon upload file CV (PDF) Anda.")
86
 
87
  try:
88
- print("--- Memulai Proses Analisis Karir ---")
89
-
 
90
  teks_cv = ekstrak_teks_dari_pdf(cv_file.name)
91
  if not teks_cv:
92
  raise gr.Error("PDF kosong atau tidak dapat dibaca.")
93
- print("βœ… Teks berhasil diekstrak.")
94
-
95
- print("2. Mengirim permintaan analisis karir ke Gemini...")
96
- prompt_analisis_karir = f"""
97
- Anda adalah seorang "Career Analyst AI". Baca teks CV dan buat laporan peluang karir dalam format JSON.
98
- Teks CV: --- {teks_cv} ---
99
- Struktur JSON yang diinginkan:
100
- - "jabatan_ideal": Jabatan paling ideal untuk kandidat.
101
- - "alasan_kecocokan": Array (list) berisi 3-4 poin MENGAPA kandidat cocok.
102
- - "deskripsi_pekerjaan": Array (list) berisi 5 poin deskripsi pekerjaan umum.
103
- - "potensi_karir": Array (list) berisi 3-4 jalur pengembangan karir.
104
- - "kisaran_gaji": Objek JSON berisi estimasi gaji untuk level "junior", "mid_level", dan "senior".
105
- - "kelebihan_tambahan": Array (list) berisi 1-2 poin saran atau kelebihan unik kandidat.
106
- Pastikan output hanya berupa JSON saja.
107
- """
108
-
109
- # βœ… Tambahan: max_output_tokens untuk membatasi token output
110
- generation_config = genai.types.GenerationConfig(
111
- response_mime_type="application/json",
112
- max_output_tokens=MAX_OUTPUT_TOKENS,
113
- )
114
- response = model.generate_content(prompt_analisis_karir, generation_config=generation_config)
115
-
116
- # βœ… Tambahan: log penggunaan token ke console
117
- log_token_usage(getattr(response, 'usage_metadata', None))
118
-
119
- print(f"πŸ“ Raw response preview: {response.text[:120]!r}")
120
- response_json = parse_json_safe(response.text)
121
- print("βœ… Laporan karir komprehensif berhasil diterima.")
122
 
123
  print("3. Membuat tautan pencarian dari hasil analisis...")
124
  keywords_from_analysis = response_json.get("jabatan_ideal", "")
125
  search_links = generate_search_links(keywords_from_analysis)
126
-
127
  response_json["tautan_pencarian"] = search_links
128
  print("βœ… Tautan pencarian ditambahkan ke JSON.")
129
 
130
- print("--- Proses Selesai ---")
131
  return response_json
132
 
133
  except Exception as e:
@@ -137,16 +103,19 @@ def analyze_career_path(cv_file):
137
  # --- MEMBUAT INTERFACE GRADIO ---
138
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
139
  gr.Markdown("# πŸš€ API Analis Peluang Karir Personal")
140
- gr.Markdown("Antarmuka ini dapat digunakan untuk pengujian. Endpoint API publik tersedia di `/run/predict` untuk integrasi ke website Anda.")
141
-
 
 
 
142
  with gr.Row():
143
  with gr.Column(scale=1):
144
  cv_pdf = gr.File(label="Upload CV (PDF) untuk Uji Coba", file_types=[".pdf"])
145
  analyze_button = gr.Button("πŸ” Analisis Karir Saya", variant="primary")
146
-
147
  with gr.Column(scale=2):
148
  output_analysis = gr.JSON(label="Output JSON dari API")
149
-
150
  analyze_button.click(
151
  fn=analyze_career_path,
152
  inputs=[cv_pdf],
 
1
  import gradio as gr
 
2
  import fitz # PyMuPDF
3
  import json
 
4
  import urllib.parse
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
  # --- KONSTANTA BATAS TOKEN OUTPUT ---
7
  MAX_OUTPUT_TOKENS = 8192
 
30
  }
31
  return links
32
 
33
+ def get_dummy_analysis(nama_kandidat="Kandidat"):
34
+ """Mengembalikan data analisis karir dummy untuk keperluan testing."""
35
+ return {
36
+ "jabatan_ideal": "Software Engineer (Backend)",
37
+ "alasan_kecocokan": [
38
+ f"{nama_kandidat} memiliki pengalaman solid dalam pengembangan backend menggunakan Python dan Node.js.",
39
+ "Portofolio menunjukkan kemampuan merancang arsitektur RESTful API yang scalable.",
40
+ "Latar belakang pendidikan di bidang Ilmu Komputer mendukung pemahaman algoritma yang kuat.",
41
+ "Pengalaman berkolaborasi dalam tim Agile menjadikan kandidat siap di lingkungan kerja modern."
42
+ ],
43
+ "deskripsi_pekerjaan": [
44
+ "Merancang, membangun, dan memelihara layanan backend yang efisien dan andal.",
45
+ "Berkolaborasi dengan tim frontend untuk mendefinisikan dan mengimplementasikan antarmuka API.",
46
+ "Melakukan code review dan memastikan standar kualitas kode terpenuhi.",
47
+ "Mengoptimalkan performa aplikasi dan query database untuk skala besar.",
48
+ "Mendokumentasikan arsitektur sistem dan proses teknis secara berkala."
49
+ ],
50
+ "potensi_karir": [
51
+ "Senior Backend Engineer dalam 2-3 tahun dengan spesialisasi di sistem terdistribusi.",
52
+ "Tech Lead atau Engineering Manager setelah membangun pengalaman kepemimpinan tim.",
53
+ "Solution Architect untuk kandidat yang tertarik pada desain sistem skala enterprise.",
54
+ "Wirausaha teknologi atau CTO di startup bidang teknologi."
55
+ ],
56
+ "kisaran_gaji": {
57
+ "junior": "Rp 6.000.000 - Rp 10.000.000 / bulan",
58
+ "mid_level": "Rp 12.000.000 - Rp 20.000.000 / bulan",
59
+ "senior": "Rp 22.000.000 - Rp 40.000.000 / bulan"
60
+ },
61
+ "kelebihan_tambahan": [
62
+ "Kandidat memiliki kontribusi aktif di GitHub yang memperkuat kredibilitas teknis secara publik.",
63
+ "Kemampuan komunikasi dalam bahasa Inggris membuka peluang karir di perusahaan multinasional atau remote global."
64
+ ]
65
+ }
66
 
67
  def analyze_career_path(cv_file):
68
+ """Fungsi utama pipeline (DUMMY): Ekstrak PDF -> Return JSON dummy -> Tambah Link."""
 
 
69
  if cv_file is None:
70
  raise gr.Error("Mohon upload file CV (PDF) Anda.")
71
 
72
  try:
73
+ print("--- [DUMMY MODE] Memulai Proses Analisis Karir ---")
74
+
75
+ # Tetap ekstrak teks PDF agar input pipeline tetap berjalan normal
76
  teks_cv = ekstrak_teks_dari_pdf(cv_file.name)
77
  if not teks_cv:
78
  raise gr.Error("PDF kosong atau tidak dapat dibaca.")
79
+ print(f"βœ… Teks berhasil diekstrak ({len(teks_cv)} karakter). [Tidak dikirim ke API]")
80
+
81
+ # Coba ambil nama dari baris pertama teks CV sebagai sentuhan personal
82
+ nama_kandidat = teks_cv.strip().splitlines()[0].strip() if teks_cv.strip() else "Kandidat"
83
+ print(f"πŸ‘€ Nama kandidat terdeteksi: {nama_kandidat}")
84
+
85
+ print("2. [DUMMY] Melewati pemanggilan Gemini API, menggunakan data dummy...")
86
+ response_json = get_dummy_analysis(nama_kandidat)
87
+ print("βœ… Data dummy berhasil disiapkan.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
 
89
  print("3. Membuat tautan pencarian dari hasil analisis...")
90
  keywords_from_analysis = response_json.get("jabatan_ideal", "")
91
  search_links = generate_search_links(keywords_from_analysis)
92
+
93
  response_json["tautan_pencarian"] = search_links
94
  print("βœ… Tautan pencarian ditambahkan ke JSON.")
95
 
96
+ print("--- [DUMMY MODE] Proses Selesai ---")
97
  return response_json
98
 
99
  except Exception as e:
 
103
  # --- MEMBUAT INTERFACE GRADIO ---
104
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
105
  gr.Markdown("# πŸš€ API Analis Peluang Karir Personal")
106
+ gr.Markdown(
107
+ "> ⚠️ **MODE TESTING (DUMMY)** β€” Output menggunakan data statis, bukan hasil dari Gemini API.\n\n"
108
+ "Antarmuka ini dapat digunakan untuk pengujian. Endpoint API publik tersedia di `/run/predict` untuk integrasi ke website Anda."
109
+ )
110
+
111
  with gr.Row():
112
  with gr.Column(scale=1):
113
  cv_pdf = gr.File(label="Upload CV (PDF) untuk Uji Coba", file_types=[".pdf"])
114
  analyze_button = gr.Button("πŸ” Analisis Karir Saya", variant="primary")
115
+
116
  with gr.Column(scale=2):
117
  output_analysis = gr.JSON(label="Output JSON dari API")
118
+
119
  analyze_button.click(
120
  fn=analyze_career_path,
121
  inputs=[cv_pdf],