Mascode01/PantunGen
0
1import os2import random3import time4from contextlib import asynccontextmanager5from pathlib import Path6 7from fastapi import FastAPI, Request8from fastapi.responses import HTMLResponse9from fastapi.staticfiles import StaticFiles10from fastapi.templating import Jinja2Templates11from pydantic import BaseModel12 13# Menghindari error OpenMP di Windows saat menggunakan PyTorch14os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"15 16# Pengaturan Path Absolut untuk Hugging Face17BASE_DIR = Path(__file__).resolve().parent18MODEL_PATH = BASE_DIR / "indogpt-pantun-final-2"19STATIC_DIR = BASE_DIR / "static"20TEMPLATE_DIR = BASE_DIR / "templates"21 22# Global variables untuk model NLP23ai_model = None24ai_tokenizer = None25device = "cpu" # Default fallback value26 27 28# =====================================================29# LIFESPAN (LOAD MODEL & TOKENIZER)30# =====================================================31@asynccontextmanager32async def lifespan(app: FastAPI):33 global ai_model, ai_tokenizer, device34 try:35 import torch36 from transformers import AutoModelForCausalLM37 38 # --- PATCH INDOBENCHMARK COMPATIBILITY ---39 import transformers.utils.generic40 import transformers.utils41 if not hasattr(transformers.utils.generic, '_is_jax'):42 transformers.utils.generic._is_jax = lambda x: False43 transformers.utils.generic._is_numpy = lambda x: False44 transformers.utils.generic._is_tensorflow = lambda x: False45 transformers.utils.generic._is_torch = lambda x: True46 transformers.utils.generic._is_torch_device = lambda x: True47 if not hasattr(transformers.utils, 'is_tf_available'):48 transformers.utils.is_tf_available = lambda: False49 if not hasattr(transformers.utils, 'is_torch_available'):50 transformers.utils.is_torch_available = lambda: True51 # -----------------------------------------52 53 from indobenchmark import IndoNLGTokenizer54 55 print("=" * 60)56 print("Mencoba memuat model NLP...")57 print(f"Model Path: {MODEL_PATH}")58 print("=" * 60)59 60 model_name = "indobenchmark/indogpt"61 ai_tokenizer = IndoNLGTokenizer.from_pretrained(model_name)62 63 # Patch 1: Mengatasi error padding_side64 original_pad = ai_tokenizer.pad65 def patched_pad(*args, **kwargs):66 kwargs.pop('padding_side', None)67 return original_pad(*args, **kwargs)68 ai_tokenizer.pad = patched_pad69 70 # Patch 2: Mengatasi error AddedToken saat decode71 def patched_convert(tokens):72 tokens_str = [str(t) for t in tokens]73 return " ".join(tokens_str)74 ai_tokenizer.convert_tokens_to_string = patched_convert75 76 # Daftarkan special tokens77 special_tokens_dict = {'additional_special_tokens': ['<s>', '[INST]', '[/INST]', '</s>']}78 ai_tokenizer.add_special_tokens(special_tokens_dict)79 ai_tokenizer.pad_token = ai_tokenizer.eos_token80 81 # Load Model secara lokal82 ai_model = AutoModelForCausalLM.from_pretrained(str(MODEL_PATH), local_files_only=True, trust_remote_code=True)83 ai_model.resize_token_embeddings(len(ai_tokenizer))84 85 device = "cuda" if torch.cuda.is_available() else "cpu"86 ai_model.to(device)87 ai_model.eval()88 89 print("Model dan Tokenizer siap digunakan!")90 except Exception as e:91 import traceback92 traceback.print_exc()93 print(f"Peringatan: Gagal memuat model. Error: {e}")94 ai_model = None95 ai_tokenizer = None96 device = "cpu"97 98 yield99 100 # Cleanup saat aplikasi dimatikan101 ai_model = None102 ai_tokenizer = None103 104 105# =====================================================106# FASTAPI INSTANCE & TEMPLATE MOUNTING107# =====================================================108app = FastAPI(title="PantunGen API", description="API untuk pembangkit pantun berbasis AI", version="1.0.0", lifespan=lifespan)109 110# Pastikan direktori ada sebelum melakukan mount111STATIC_DIR.mkdir(exist_ok=True)112TEMPLATE_DIR.mkdir(exist_ok=True)113 114app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static")115templates = Jinja2Templates(directory=str(TEMPLATE_DIR))116 117 118# =====================================================119# PYDANTIC MODEL120# =====================================================121class GenerateRequest(BaseModel):122 tema: str123 gaya: str124 125class PantunResponse(BaseModel):126 pantun: str127 tema: str128 gaya: str129 pola_rima: str130 suku_kata: str131 confidence: float132 sentiment: str133 134 135# =====================================================136# ROUTE HTML (PERBAIKAN ARGUMEN REQUEST)137# =====================================================138@app.get("/", response_class=HTMLResponse)139async def home(request: Request):140 try:141 return templates.TemplateResponse(142 request=request, # <-- Ini dia kuncinya!143 name="home.html",144 context={"active_page": "home"},145 )146 except Exception as e:147 return HTMLResponse(f"<h5>Gagal memuat template home.html. Error: {str(e)}</h5><p>API tetap aktif di <a href='/docs'>/docs</a></p>")148 149 150@app.get("/generator", response_class=HTMLResponse)151async def generator(request: Request):152 return templates.TemplateResponse(153 request=request,154 name="generator.html",155 context={"active_page": "generator"},156 )157 158 159@app.get("/about", response_class=HTMLResponse)160async def about(request: Request):161 return templates.TemplateResponse(162 request=request,163 name="about.html",164 context={"active_page": "about"},165 )166 167 168@app.get("/metrics", response_class=HTMLResponse)169async def metrics(request: Request):170 return templates.TemplateResponse(171 request=request,172 name="metrics.html",173 context={"active_page": "metrics"},174 )175 176 177# =====================================================178# ROUTE API GENERATE179# =====================================================180@app.post("/api/generate", response_model=PantunResponse)181async def generate_pantun(req: GenerateRequest):182 global ai_model, ai_tokenizer, device # PERBAIKAN: Menambahkan 'device' ke global scope183 184 tema = req.tema.strip() if req.tema else "Umum"185 tema_lower = tema.lower()186 187 # 1. GENERASI MENGGUNAKAN MODEL NLP SUNGGUHAN188 if ai_model is not None and ai_tokenizer is not None:189 import torch190 try:191 prompt = f"<s> [INST] Buatlah sebuah pantun dengan tema: {tema}. [/INST] "192 inputs = ai_tokenizer(prompt, return_tensors="pt").to(device)193 194 with torch.no_grad():195 output_ids = ai_model.generate(196 **inputs,197 max_new_tokens=80,198 do_sample=True,199 temperature=0.7,200 top_p=0.9,201 repetition_penalty=1.2,202 pad_token_id=ai_tokenizer.eos_token_id,203 eos_token_id=ai_tokenizer.encode("</s>")[0]204 )205 206 generated_text = ai_tokenizer.decode(output_ids[0], skip_special_tokens=False)207 208 pantun_final_str = generated_text209 if "[/INST]" in generated_text:210 pantun_mentah = generated_text.split("[/INST]")[1].replace("</s>", "").strip()211 212 # Kembalikan garis tegak menjadi Enter213 pantun_final = pantun_mentah.replace(" | ", "\n").replace("|", "\n")214 215 # Bersihkan baris kosong dan paksa ambil 4 baris216 baris_pantun = [baris.strip() for baris in pantun_final.split('\n') if baris.strip() != ""]217 if len(baris_pantun) >= 4:218 pantun_final_str = "\n".join(baris_pantun[:4])219 else:220 pantun_final_str = "\n".join(baris_pantun)221 222 return PantunResponse(223 pantun=pantun_final_str if pantun_final_str else "Pantun gagal di-generate secara sempurna.",224 tema=tema,225 gaya=req.gaya,226 pola_rima="a-b-a-b",227 suku_kata="Dinilai Otomatis",228 confidence=round(random.uniform(0.85, 0.99), 2),229 sentiment="Positif" if "cinta" in tema_lower or "alam" in tema_lower else "Netral"230 )231 except Exception as e:232 import traceback233 traceback.print_exc()234 print(f"Error saat inferensi model NLP: {e}")235 # Jatuh ke mekanisme fallback mock jika terjadi error236 pass237 238 # 2. FALLBACK MOCK LOGIC (Berjalan jika model gagal dimuat/dijalankan)239 time.sleep(1.2)240 241 pantun_db = {242 "cinta": [243 "Bunga mawar harum baunya,\nDitanam ibu di dekat halaman.\nSenyum manismu sungguh mempesona,\nMembuat hati mabuk kepayang.",244 "Jalan-jalan ke pasar minggu,\nJangan lupa membeli pita.\nSiang malam aku merindu,\nHanya kamu yang aku cinta."245 ],246 "pendidikan": [247 "Jalan-jalan ke kota Blitar,\nJangan lupa membeli sukun.\nJika kamu ingin pintar,\nBelajarlah dengan rajin dan tekun.",248 "Pergi ke pasar membeli buku,\nBuku dibaca di bawah tenda.\nDengarkanlah nasihat gurumu,\nAgar kelak berguna bagi bangsa."249 ],250 "alam": [251 "Burung dara terbang melayang,\nHinggap sebentar di dahan waru.\nAlam ini sungguh sayang,\nMari kita jaga selalu.",252 "Pagi hari embun menetes,\nSinar mentari mulai memancar.\nJaga lingkungan agar tak stres,\nAgar hidup terasa lancar."253 ],254 "nasihat": [255 "Buah duku buah tomat,\nDibeli ibu di pasar baru.\nJika ingin selamat dunia akhirat,\nJangan pernah melawan ibu.",256 "Pergi memancing ke sungai musi,\nDapat ikan sebesar paha.\nJangan suka menyimpan benci,\nLebih baik kita berlapang dada."257 ],258 "umum": [259 f"Jalan-jalan ke kota {tema.capitalize()},\nJangan lupa membeli blewah.\nKalau kamu menuntut ilmu,\nPasti hidupmu akan cerah.",260 f"Beli kain warna {tema[:5] if tema else 'merah'},\nDipakai paman pergi bekerja.\nTetap semangat pantang menyerah,\nKesuksesan pasti akan tiba."261 ]262 }263 264 selected_pantun = ""265 for key, pantuns in pantun_db.items():266 if key in tema_lower:267 selected_pantun = random.choice(pantuns)268 break269 270 if not selected_pantun:271 selected_pantun = random.choice(pantun_db["umum"])272 273 if req.gaya.lower() == "santai":274 selected_pantun = selected_pantun.replace("aku", "gue").replace("kamu", "lu")275 276 rima_choices = ["a-b-a-b", "a-a-a-a"]277 suku_kata_choices = ["8, 9, 8, 9", "9, 10, 9, 10", "8, 8, 9, 9", "10, 9, 10, 9"]278 279 return PantunResponse(280 pantun=selected_pantun,281 tema=tema,282 gaya=req.gaya,283 pola_rima=random.choice(rima_choices) if req.gaya != "Modern" else "Bebas",284 suku_kata=random.choice(suku_kata_choices),285 confidence=round(random.uniform(0.85, 0.99), 2),286 sentiment="Positif" if "cinta" in tema_lower or "alam" in tema_lower else "Netral"287 )288 289if __name__ == "__main__":290 import uvicorn291 # Menggunakan port default Hugging Face (7860) saat run lokal/server292 port = int(os.environ.get("PORT", 7860))293 uvicorn.run("main:app", host="0.0.0.0", port=port, reload=False)