import os import time import gradio as gr from huggingface_hub import hf_hub_download from llama_cpp import Llama GGUF_REPO = os.environ.get("GGUF_REPO", "ggapar/KomdigiITS-8B-DFK-GGUF") GGUF_FILENAME = os.environ.get("GGUF_FILENAME", "model-q4_k_m.gguf") HF_TOKEN = os.environ.get("HF_TOKEN") PROMPT_FAST = ( "Anda adalah sistem klasifikasi konten bahasa Indonesia. " "Klasifikasikan teks ke dalam satu dari lima kategori: " "Fakta, Disinformasi, Fitnah, Ujaran Kebencian, Non-DFK. " "Jawab HANYA dengan nama kategori, tanpa penjelasan." ) PROMPT_FULL = ( "Anda adalah sistem analisis konten yang mendeteksi disinformasi, fitnah, " "dan ujaran kebencian dalam teks bahasa Indonesia. " "Untuk setiap teks, berikan:\n" "1. Klasifikasi: Fakta, Disinformasi, Fitnah, Ujaran Kebencian, atau Non-DFK\n" "2. Penalaran singkat dan terstruktur (maksimal 3-4 poin)\n\n" "Format output WAJIB:\n" "[LABEL] {nama kategori}\n" "[REASONING]\n" "{poin-poin penalaran}" ) LABEL_COLORS = { "Fakta": "#22c55e", "Disinformasi": "#ef4444", "Fitnah": "#f97316", "Ujaran Kebencian": "#dc2626", "Non-DFK": "#6b7280", } VALID_LABELS = set(LABEL_COLORS.keys()) EXAMPLES = [ ["Pemerintah Indonesia berhasil menurunkan angka kemiskinan menjadi 9% pada 2024."], ["Vaksin COVID-19 mengandung chip 5G yang bisa dikendalikan dari jarak jauh."], ["Si A adalah koruptor yang mencuri miliaran uang rakyat meskipun belum terbukti."], ["Semua warga suku X itu malas dan tidak bisa dipercaya dalam pekerjaan apapun."], ["Hari ini cuaca di Jakarta cukup panas dengan suhu mencapai 32 derajat Celsius."], ] # ── load model ─────────────────────────────────────────────────────────────── print(f"Downloading GGUF: {GGUF_REPO}/{GGUF_FILENAME} ...") model_path = hf_hub_download( repo_id=GGUF_REPO, filename=GGUF_FILENAME, token=HF_TOKEN, ) print("Loading model ...") llm = Llama( model_path=model_path, n_ctx=2048, n_threads=2, n_batch=512, # batch lebih besar = prefill lebih cepat n_gpu_layers=0, verbose=False, ) print("Model siap!") # ── parsing output ──────────────────────────────────────────────────────────── def parse_output(raw: str): label = "—" reasoning = "" lines = raw.strip().splitlines() # Coba parse format [LABEL] / [REASONING] reasoning_start = 0 for i, line in enumerate(lines): if line.upper().strip().startswith("[LABEL]"): candidate = line[len("[LABEL]"):].strip() for valid in VALID_LABELS: if valid.lower() in candidate.lower(): label = valid break if label == "—": label = candidate reasoning_start = i + 1 break # Jika tidak ada [LABEL] marker (mode cepat), cari langsung if reasoning_start == 0: for valid in VALID_LABELS: if valid.lower() in raw.lower(): label = valid break return label, "" for i, line in enumerate(lines[reasoning_start:], start=reasoning_start): if "[REASONING]" in line.upper(): reasoning = "\n".join(lines[i + 1:]).strip() return label, reasoning reasoning = "\n".join(lines[reasoning_start:]).strip() return label, reasoning # ── inference ──────────────────────────────────────────────────────────────── def classify_text(text: str, mode: str): if not text.strip(): return "—", "", "", "" is_fast = "Cepat" in mode system_prompt = PROMPT_FAST if is_fast else PROMPT_FULL max_tokens = 15 if is_fast else 350 t0 = time.time() response = llm.create_chat_completion( messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": text.strip()}, ], max_tokens=max_tokens, temperature=0.1, repeat_penalty=1.1, stop=["<|im_end|>", ""], ) elapsed = time.time() - t0 raw = response["choices"][0]["message"]["content"].strip() label, reasoning = parse_output(raw) color = LABEL_COLORS.get(label, "#6b7280") badge = ( f'