Team Ai
Apppublic

deankk/CapGen-AI

sourceHugging Facemitupdated 4mo agoView on Hugging Face
0likes
app.py432 linesDownload Raw Back to root
1"""2✍️ CapGen AI — Caption Generator Indonesia (v2)3Versi improved: few-shot prompting + generate 1 per 14"""5 6import gradio as gr7import torch8from transformers import AutoTokenizer, AutoModelForCausalLM9 10MODEL_NAME = "Qwen/Qwen2.5-3B-Instruct"11 12print(f"⏳ Loading {MODEL_NAME}...")13tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME, trust_remote_code=True)14model = AutoModelForCausalLM.from_pretrained(15    MODEL_NAME,16    torch_dtype=torch.bfloat16,17    low_cpu_mem_usage=True,18    trust_remote_code=True,19)20model.eval()21print("✅ Model siap!")22 23# ══════════════════════════════════════════════════════24#  CONTOH CAPTION PER GAYA (ini kunci kualitasnya)25# ══════════════════════════════════════════════════════26 27CONTOH = {28    "Aesthetic": {29        "Instagram": (30            "Contoh caption bagus:\n"31            '"Di antara decitan kerupuk dan aroma rempah, hidup rasanya lengkap 🌾✨\n'32            '#warungcita #hidupsimple #nungguinmatahari"\n'33            '"Senja kali ini bilang, kamu boleh lelah tapi jangan berhenti 🌅\n'34            '#senja #istirahatsejenak #catatanpetang"'35        ),36        "TikTok": (37            "Contoh caption bagus:\n"38            '"Senja ngomong kamu cantik hari ini 🧡"\n'39            '"Yang penting bahagia, sisanya urusan nanti 🌿"'40        ),41        "default": (42            "Contoh caption bagus:\n"43            '"Hal kecil yang bikin senyum tanpa alasan 🌿✨\n'44            '#simplejoy #catatanhariini"\n'45            '"Beberapa hal indah datang tanpa direncanakan 🌙\n'46            '#unexpected #grateful"'47        ),48    },49    "Lucu": {50        "Instagram": (51            "Contoh caption bagus:\n"52            '"Diet mulai Senin... Senin apa ya yang mana 🤔\n'53            '#dietgagal #makanyangpenting #realita"\n'54            '"Hidupku kayak WiFi kantoran, kadang nyambung kadang nggak 📶😅\n'55            '#hidupini #relatable #galau"'56        ),57        "default": (58            "Contoh caption bagus:\n"59            '"Udah usaha maksimal, hasilnya tetap minimal 🤡"\n'60            '"Mau kaya tapi males, gimana tuh 😭"'61        ),62    },63    "Jualan": {64        "Instagram": (65            "Contoh caption bagus:\n"66            '"PROMO TERBATAS! 🔥 Serum glow yang biasanya 150rb, sekarang CUMA 89rb!\n'67            'Stok terbatas, siapa cepat dia dapat 💕\n'68            'Order via DM / klik link di bio!\n'69            '#skincare #serumglow #promoskincare #diskon"\n'70            '"Best seller kami kembali hadir! ✨ Celana linin premium yang nyaman seharian.\n'71            'Harga normal 199rb → NOW 129rb aja!\n'72            'Tersisa 30 pcs saja, buruan!\n'73            '#celanalinen #fashionpria #diskon"'74        ),75        "TikTok": (76            "Contoh caption bagus:\n"77            '"Harga 89rb aja buat kulit glowing! Link di bio 🔗🔥"'78        ),79        "default": (80            "Contoh caption bagus:\n"81            '"Promo hari ini! Dari 150rb jadi 89rb aja 🔥\n'82            'Order langsung via DM ya!\n'83            '#promohariini #diskon"'84        ),85    },86    "Motivasi": {87        "default": (88            "Contoh caption bagus:\n"89            '"Proses itu berat, tapi hasilnya buat kamu bangun bangun pagi 🔥\n'90            '#motivasipagi #hustle #nevergiveup"\n'91            '"Kamu bukan lambat, kamu lagi build fondasi yang kuat 💪\n'92            '#proses #trusttheprocess #growth"'93        ),94    },95    "Romantis": {96        "default": (97            "Contoh caption bagus:\n"98            '"Kamu tuh kayak kopi pagi, tanpa kamu seharian rasanya hambar ☕💕\n'99            '#pacarsayang #romantis #cintalokal"\n'100            '"Senyum kamu itu autofokus, yang lain jadi blur semua 📸💗\n'101            '#jodoh #romance #lovestory"'102        ),103    },104    "Gaul/Jaksel": {105        "default": (106            "Contoh caption bagus:\n"107            '"Literally this is the best day ever bangeet bestie 🫶✨\n'108            '#bestie #jaksel #vibeskekinian"\n'109            '"Makanannya literally bikin crying aaah parah banget sih 🍜😭\n'110            '#foodie #makanenak #gaul"'111        ),112    },113    "Santai": {114        "default": (115            "Contoh caption bagus:\n"116            '"Hari ini ngga ada rencana spesial, tapi ngga apa-apa kok 🌿\n'117            '#chill #santai #flow"\n'118            '"Nongkrong, ngopi, ngobrol ga jelas. Tapi bahagia ☕😊\n'119            '#weekendvibes #hangout #qualitytime"'120        ),121    },122    "Formal": {123        "default": (124            "Contoh caption bagus:\n"125            '"Kami mengucapkan terima kasih atas kehadiran Bapak/Ibu dalam acara seminar nasional ini.\n'126            'Semoga ilmu yang diperoleh dapat bermanfaat.\n'127            '#seminar #profesional #diknas"\n'128            '"Peluncuran program terbaru kami telah berlangsung dengan baik.\n'129            'Kami berharap program ini dapat memberikan dampak positif bagi masyarakat.\n'130            '#programbaru #layananpublik"'131        ),132    },133}134 135# Fallback kalau gaya/platform ga ada di contoh136def get_contoh(gaya, platform):137    if gaya in CONTOH:138        if platform in CONTOH[gaya]:139            return CONTOH[gaya][platform]140        return CONTOH[gaya]["default"]141    return CONTOH["Aesthetic"]["default"]142 143 144PLATFORMS = {145    "Instagram":        "Maks 2200 karakter. Boleh panjang, emoji bebas, hashtag penting.",146    "TikTok":           "Maks 150 karakter. SANGAT pendek, catchy.",147    "Twitter/X":        "Maks 280 karakter. Padat dan tajam.",148    "LinkedIn":         "Maks 3000 karakter. Profesional tapi engaging.",149    "WhatsApp Status":  "Maks 100 karakter. Sangat singkat.",150}151 152STYLES = {153    "Aesthetic":    "Puitis, minimalis emoji, kata-kata indah",154    "Lucu":         "Punchline di akhir, bahasa slang, relatable",155    "Jualan":       "Ada harga/CTA, urgency, benefit jelas",156    "Motivasi":     "Inspiratif, powerful words, emoji api",157    "Romantis":     "Manis, puitis, emoji hati",158    "Gaul/Jaksel":  "Slang Jakarta: literally, bestie, bangeet",159    "Santai":       "Casual, kayak ngobrol sama temen",160    "Formal":       "Bahasa baku, sopan",161}162 163LANGUAGES = {164    "Indonesia":       "Bahasa Indonesia standar",165    "Indonesia Gaul":  "Slang gaul kekinian",166    "Bahasa Jawa":     "Bahasa Jawa (ngoko/krama)",167    "Bahasa Sunda":    "Bahasa Sunda gaul",168    "English":         "Bahasa Inggris natural",169    "Bilingual":       "Campuran Indonesia dan English",170}171 172PRESETS = {173    "🍜 Makanan":    "Foto makan nasi goreng spesial di warung pinggir jalan, porsi besar, harga cuma 15rb, ada telur dan kerupuk",174    "🌅 Sunset":     "Foto sunset di pantai Bali, langit oranye kemerahan, pegang minuman kelapa, rambut diterpa angin",175    "🛒 Jualan":     "Foto produk skincare baru, botol kaca bening, tekstur gel, harga normal 150rb promo jadi 89rb",176    "🏢 Kantor":     "Foto bareng tim kantor setelah selesai project besar, semua senang, pakai seragam kompak",177    "🐱 Kucing":     "Foto kucing tidur miring di bantal pink, lucu banget, bulunya lembut dan fluffy",178    "🛵 Jalan-jalan": "Foto naik motor melalui jalan Malioboro malam hari, lampu warna-warni, habis belanja batik",179    "🏋️ Gym":        "Foto habis workout di gym, badan berkeringat, baju sport, terlihat bugar dan proud",180    "📚 Belajar":    "Foto meja belajar yang rapi, ada buku dan kopi, lampu belajar warm light, vibes produktif",181}182 183# ══════════════════════════════════════════════════════184#  LOGIC185# ══════════════════════════════════════════════════════186 187def generate_text(messages):188    text = tokenizer.apply_chat_template(189        messages, tokenize=False, add_generation_prompt=True190    )191    inputs = tokenizer(text, return_tensors="pt")192    with torch.no_grad():193        outputs = model.generate(194            **inputs,195            max_new_tokens=300,196            temperature=0.7,197            top_p=0.85,198            top_k=50,199            do_sample=True,200            repetition_penalty=1.2,201        )202    new_tokens = outputs[0][inputs["input_ids"].shape[-1]:]203    return tokenizer.decode(new_tokens, skip_special_tokens=True).strip()204 205 206def build_prompt(konteks, platform, gaya, bahasa):207    contoh = get_contoh(gaya, platform)208    return (209        f"Buat SATU caption untuk:\n\n"210        f"Platform: {platform} ({PLATFORMS[platform]})\n"211        f"Gaya: {gaya} ({STYLES[gaya]})\n"212        f"Bahasa: {bahasa} ({LANGUAGES[bahasa]})\n"213        f"Konteks: {konteks.strip()}\n\n"214        f"{contoh}\n\n"215        f"Sekarang buat SATU caption baru yang BERBEDA dari contoh di atas, "216        f"untuk konteks yang saya berikan. Langsung tulis caption-nya saja, "217        f"sertakan 3-6 hashtag di akhir."218    )219 220 221def clean_output(text):222    """Bersihkan output dari kalimat pembuka yang tidak perlu"""223    lines_to_remove = [224        "Baik, berikut captionnya:", "Berikut captionnya:",225        "Berikut:", "Tentu, berikut:", "Oke, berikut:",226        "Baik,", "Tentu,", "Oke,", "Here is", "Sure, here",227        "Berikut adalah caption yang kamu minta:",228        "Ini captionnya:", "Nih captionnya:",229    ]230    for line in lines_to_remove:231        if text.strip().startswith(line):232            text = text.strip()[len(line):].strip()233            if text.startswith(":"):234                text = text[1:].strip()235    return text.strip()236 237 238def generate_caption(konteks, platform, gaya, bahasa):239    if not konteks or not konteks.strip():240        return "⚠️ Tulis dulu deskripsi foto/konten kamu!"241 242    system_msg = (243        "Kamu adalah penulis caption media sosial profesional bahasa Indonesia. "244        "Kamu HANYA mengeluarkan caption. Tidak ada penjelasan, tidak ada pembuka, "245        "tidak ada kata 'Berikut'. Langsung tulis caption saja."246    )247    user_msg = build_prompt(konteks, platform, gaya, bahasa)248 249    messages = [250        {"role": "system", "content": system_msg},251        {"role": "user", "content": user_msg}252    ]253 254    try:255        result = generate_text(messages)256    except Exception as e:257        return f"❌ Error: {str(e)[:200]}"258 259    return clean_output(result)260 261 262def fill_preset(name):263    return PRESETS.get(name, "")264 265 266# ══════════════════════════════════════════════════════267#  UI — DIUBAH: Generate 1 caption, bisa klik berkali-kali268# ══════════════════════════════════════════════════════269 270CUSTOM_CSS = """271#btn-generate {272    font-size: 18px !important;273    font-weight: 600 !important;274    padding: 14px 32px !important;275    border-radius: 12px !important;276}277.output-box textarea {278    font-size: 16px !important;279    line-height: 1.8 !important;280    border-radius: 10px !important;281    min-height: 120px !important;282}283.preset-row button {284    min-width: fit-content !important;285    font-size: 13px !important;286}287.footer-text {288    text-align: center;289    color: #aaa;290    font-size: 12px;291    margin-top: 16px;292}293.hint-text {294    font-size: 13px;295    color: #888;296    margin-top: 4px;297}298"""299 300with gr.Blocks() as demo:301 302    gr.Markdown("""303    <div style="text-align:center; padding:8px 0 4px 0;">304        <h1 style="margin:0;">✍️ CapGen AI</h1>305        <p style="font-size:17px; color:#555; margin:4px 0 0 0;">306            Caption Generator Khusus Bahasa Indonesia307        </p>308        <p style="font-size:13px; color:#999; margin:2px 0 0 0;">309            Gak perlu login · Gak perlu token · Langsung kepakai310        </p>311    </div>312    """)313 314    with gr.Row(equal_height=False):315 316        with gr.Column(scale=1):317 318            gr.Markdown("### 📝 Ceritakan Foto/Konten Kamu")319            konteks = gr.Textbox(320                placeholder=(321                    "Contoh: Foto makan nasi goreng di warung pinggir jalan, "322                    "porsi besar, harga 15rb, ada telur mata sapi..."323                ),324                lines=4,325                max_lines=8,326                show_label=False,327            )328 329            gr.Markdown("### ⚡ Contoh Cepat (klik = langsung isi)")330            preset_names = list(PRESETS.keys())331            for row_start in range(0, len(preset_names), 4):332                with gr.Row(elem_classes=["preset-row"]):333                    for name in preset_names[row_start:row_start + 4]:334                        gr.Button(name, size="sm", variant="secondary").click(335                            fn=lambda n=name: fill_preset(n),336                            outputs=konteks,337                        )338 339            gr.Markdown("### ⚙️ Pengaturan")340            with gr.Row():341                platform = gr.Dropdown(342                    choices=list(PLATFORMS.keys()),343                    value="Instagram",344                    label="📱 Platform",345                )346                gaya = gr.Dropdown(347                    choices=list(STYLES.keys()),348                    value="Aesthetic",349                    label="🎨 Gaya",350                )351 352            with gr.Row():353                bahasa = gr.Dropdown(354                    choices=list(LANGUAGES.keys()),355                    value="Indonesia",356                    label="🌐 Bahasa",357                )358 359            btn_generate = gr.Button(360                "✨ Generate Caption",361                variant="primary",362                size="lg",363                elem_id="btn-generate",364            )365            gr.Markdown(366                '<p class="hint-text">💡 Klik berkali-kali untuk hasil berbeda-beda</p>'367            )368 369        with gr.Column(scale=1):370            gr.Markdown("### 📋 Caption Kamu")371 372            output_caption = gr.Textbox(373                label="",374                lines=5,375                max_lines=10,376                interactive=False,377                show_label=False,378                placeholder="Hasil caption akan muncul di sini...",379                elem_classes=["output-box"],380            )381 382            gr.Markdown(383                '<p class="hint-text">Blok teks di atas lalu Ctrl+C / Cmd+C untuk copy</p>'384            )385 386            with gr.Accordion("📜 Riwayat Caption", open=False):387                history = gr.Textbox(388                    label="",389                    lines=12,390                    interactive=False,391                    show_label=False,392                    placeholder="Caption-caption sebelumnya akan muncul di sini...",393                )394 395    # State untuk riwayat396    history_state = gr.State("")397 398    def generate_with_history(konteks, platform, gaya, bahasa, prev_history):399        caption = generate_caption(konteks, platform, gaya, bahasa)400 401        if not caption.startswith("⚠️") and not caption.startswith("❌"):402            separator = "\n" + "─" * 40 + "\n"403            new_history = f"[{platform} · {gaya} · {bahasa}]\n{caption}{separator}{prev_history}"404            return caption, new_history405        return caption, prev_history406 407    btn_generate.click(408        fn=generate_with_history,409        inputs=[konteks, platform, gaya, bahasa, history_state],410        outputs=[output_caption, history_state],411    )412    btn_generate.click(413        fn=lambda h: h,414        inputs=[history_state],415        outputs=[history],416    )417 418    gr.Markdown(419        '<div class="footer-text">'420        "Dibuat dengan ❤️ untuk komunitas Indonesia · "421        f"Model: {MODEL_NAME}"422        "</div>"423    )424 425demo.launch(426    theme=gr.themes.Soft(427        primary_hue="orange",428        secondary_hue="amber",429        neutral_hue="stone",430    ),431    css=CUSTOM_CSS,432)