irhamni commited on
Commit
2df7b5a
·
verified ·
1 Parent(s): 3a2cd4b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +101 -4
app.py CHANGED
@@ -1447,6 +1447,104 @@ def build_kpi_markdown(summary_jenis: pd.DataFrame, agg_total: pd.DataFrame, agg
1447
  # 14) LLM + WORD
1448
  # ============================================================
1449
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1450
  def generate_word_report(wilayah, summary_jenis, agg_total, agg_jenis, analysis_text):
1451
  doc = Document()
1452
  doc.add_heading(f"Laporan IPLM — {wilayah}", level=1)
@@ -1507,15 +1605,14 @@ def generate_word_report(wilayah, summary_jenis, agg_total, agg_jenis, analysis_
1507
  doc.add_heading("Metodologi", level=2)
1508
  doc.add_paragraph(
1509
  "Indeks dasar dihitung per entitas menggunakan transformasi Yeo-Johnson dan normalisasi MinMax nasional per indikator. "
1510
- "Nilai kemudian diagregasi per wilayah×jenis untuk memperoleh Indeks Dasar wilayah per jenis."
1511
  )
1512
  doc.add_paragraph(
1513
  "Penyesuaian dilakukan berbasis kecukupan sampel minimum 68% pada level wilayah, "
1514
- "dengan rumus faktor = min(total_terkumpul / target_total_68, 1.0). "
1515
- "Indeks_Final_wilayah×jenis = Indeks_Dasar_Agregat × faktor."
1516
  )
1517
  doc.add_paragraph(
1518
- "Nilai keseluruhan wilayah (FIX) dihitung sebagai rata-rata 3 jenis (sekolah+umum+khusus) ÷ 3, dengan missing dianggap 0."
1519
  )
1520
 
1521
  doc.add_heading("Analisis Naratif (LLM)", level=2)
 
1447
  # 14) LLM + WORD
1448
  # ============================================================
1449
 
1450
+ _HF_CLIENT = None
1451
+
1452
+ def get_llm_client():
1453
+ global _HF_CLIENT
1454
+ if _HF_CLIENT is not None:
1455
+ return _HF_CLIENT
1456
+ try:
1457
+ _HF_CLIENT = InferenceClient(model=LLM_MODEL_NAME, token=HF_TOKEN) if HF_TOKEN else InferenceClient(model=LLM_MODEL_NAME)
1458
+ return _HF_CLIENT
1459
+ except Exception:
1460
+ _HF_CLIENT = None
1461
+ return None
1462
+
1463
+
1464
+ def build_context(summary_jenis: pd.DataFrame, agg_total: pd.DataFrame, verif_total: pd.DataFrame, wilayah: str, kew: str) -> str:
1465
+ lines = []
1466
+ lines.append(f"Wilayah filter: {wilayah}")
1467
+ lines.append(f"Kewenangan: {kew}")
1468
+ lines.append("Metode: Indeks dasar dihitung per entitas (Yeo-Johnson + MinMax nasional per indikator), lalu diagregasi per wilayah×jenis.")
1469
+ lines.append("Penyesuaian: faktor = min(total_terkumpul / target_total_68, 1.0).")
1470
+ lines.append("FIX keseluruhan: nilai keseluruhan = rata-rata 3 jenis (sekolah+umum+khusus) ÷ 3 (missing=0, tetap ÷3).")
1471
+
1472
+ if summary_jenis is not None and not summary_jenis.empty:
1473
+ lines.append("\nRingkasan (jenis + keseluruhan):")
1474
+ for _, r in summary_jenis.iterrows():
1475
+ try:
1476
+ jenis = str(r.get("Jenis", "")).strip()
1477
+ jw = int(pd.to_numeric(r.get("Jumlah_Wilayah", 0), errors="coerce") or 0)
1478
+ tp = int(pd.to_numeric(r.get("Total_Perpus", 0), errors="coerce") or 0)
1479
+ fin = float(pd.to_numeric(r.get("Indeks_Final_Disesuaikan_0_100", 0), errors="coerce") or 0)
1480
+ das = float(pd.to_numeric(r.get("Indeks_Dasar_0_100", 0), errors="coerce") or 0)
1481
+ cov = float(pd.to_numeric(r.get("Coverage_Target68_Jenis_%", 0), errors="coerce") or 0)
1482
+ lines.append(f"- {jenis}: wilayah={jw}, total_perpus={tp}, dasar={das:.2f}, final={fin:.2f}, coverage_target68={cov:.2f}%")
1483
+ except Exception:
1484
+ continue
1485
+
1486
+ if agg_total is not None and not agg_total.empty:
1487
+ label_col = "Kab/Kota" if "Kab/Kota" in agg_total.columns else ("Provinsi" if "Provinsi" in agg_total.columns else None)
1488
+ if label_col:
1489
+ lines.append("\nTop 5 wilayah (Final tertinggi):")
1490
+ top = agg_total.sort_values("Indeks_Final_Wilayah_0_100", ascending=False).head(5)
1491
+ for _, r in top.iterrows():
1492
+ wl = str(r.get(label_col, "(wilayah)"))
1493
+ fin = float(pd.to_numeric(r.get("Indeks_Final_Wilayah_0_100", 0), errors="coerce") or 0)
1494
+ lines.append(f"- {wl}: Final={fin:.2f}")
1495
+
1496
+ return "\n".join(lines)
1497
+
1498
+
1499
+ def generate_llm_analysis(summary_jenis, agg_total, verif_total, wilayah, kew):
1500
+ ctx = build_context(summary_jenis, agg_total, verif_total, wilayah, kew)
1501
+
1502
+ # kalau LLM dimatikan / token gak ada -> return teks aman
1503
+ client = get_llm_client()
1504
+ if (client is None) or (not USE_LLM):
1505
+ return (
1506
+ "Analisis otomatis (LLM) tidak tersedia.\n\n"
1507
+ "Catatan: Set USE_LLM=True dan pastikan HF_TOKEN tersedia bila ingin mengaktifkan analisis LLM."
1508
+ )
1509
+
1510
+ system_prompt = (
1511
+ "Anda adalah analis kebijakan perpustakaan dan literasi di Indonesia. "
1512
+ "Tugas Anda menyusun analisis berbasis data IPLM secara formal, tajam, dan operasional."
1513
+ )
1514
+
1515
+ user_prompt = f"""
1516
+ DATA RINGKAS IPLM:
1517
+
1518
+ {ctx}
1519
+
1520
+ TULISKAN ANALISIS BAHASA INDONESIA FORMAL, STRUKTUR:
1521
+ 1) Gambaran umum hasil wilayah (1 paragraf).
1522
+ 2) Analisis jenis sekolah, umum, khusus serta indeks keseluruhan (2 paragraf).
1523
+ 3) Penjelasan makna penyesuaian berbasis target 68% (1 paragraf, netral).
1524
+ 4) Rekomendasi program 3–5 tahun (2 paragraf, konkret dan dapat dieksekusi).
1525
+
1526
+ ATURAN:
1527
+ - Jangan memakai label eksplisit "rendah/sedang/tinggi".
1528
+ - Gunakan frasa netral: "memerlukan penguatan", "memerlukan konsolidasi", dsb.
1529
+ """
1530
+
1531
+ try:
1532
+ resp = client.chat_completion(
1533
+ model=LLM_MODEL_NAME,
1534
+ messages=[
1535
+ {"role": "system", "content": system_prompt},
1536
+ {"role": "user", "content": user_prompt},
1537
+ ],
1538
+ max_tokens=1100,
1539
+ temperature=0.25,
1540
+ top_p=0.9,
1541
+ )
1542
+ text = resp.choices[0].message.content.strip()
1543
+ return text if text else "LLM mengembalikan respon kosong."
1544
+ except Exception as e:
1545
+ return f"⚠️ Error saat memanggil LLM: {repr(e)}"
1546
+
1547
+
1548
  def generate_word_report(wilayah, summary_jenis, agg_total, agg_jenis, analysis_text):
1549
  doc = Document()
1550
  doc.add_heading(f"Laporan IPLM — {wilayah}", level=1)
 
1605
  doc.add_heading("Metodologi", level=2)
1606
  doc.add_paragraph(
1607
  "Indeks dasar dihitung per entitas menggunakan transformasi Yeo-Johnson dan normalisasi MinMax nasional per indikator. "
1608
+ "Nilai kemudian diagregasi per wilayah×jenis."
1609
  )
1610
  doc.add_paragraph(
1611
  "Penyesuaian dilakukan berbasis kecukupan sampel minimum 68% pada level wilayah, "
1612
+ "dengan rumus faktor = min(total_terkumpul / target_total_68, 1.0)."
 
1613
  )
1614
  doc.add_paragraph(
1615
+ "Nilai keseluruhan (FIX) dihitung sebagai rata-rata 3 jenis (sekolah+umum+khusus) ÷ 3, dengan missing dianggap 0."
1616
  )
1617
 
1618
  doc.add_heading("Analisis Naratif (LLM)", level=2)