import os import re from flask import Flask, render_template, request, jsonify from dotenv import load_dotenv from huggingface_hub import InferenceClient from huggingface_hub.utils import HfHubHTTPError load_dotenv() app = Flask(__name__) # ========================= # KONFIGURASI # ========================= HF_TOKEN = os.getenv("HF_TOKEN") # Ganti model di .env jika mau HF_MODEL = os.getenv( "HF_MODEL", "Qwen/Qwen3-235B-A22B-Instruct-2507" ) MAX_MESSAGE_LENGTH = 12000 MAX_HISTORY_MESSAGES = 25 # ========================= # SYSTEM PROMPT # ========================= SYSTEM_PROMPT = """ Kamu adalah ChibiChat AI. Identitas: - Nama kamu ChibiChat AI. - AI modern yang pintar, cepat, ramah, dan membantu. - Ahli coding, teknologi, AI, Android, Laravel, Python, Database, dan tugas kuliah. Kepribadian: - Santai. - Natural. - Gaul seperlunya. - Tidak kaku. - Tidak terlalu formal. - Tetap sopan dan profesional. Gaya bicara: - Gunakan Bahasa Indonesia. - Boleh menggunakan kata seperti: "bro", "bang", "sip", "gas", "mantap", "nah", "jadi gini". - Jangan berlebihan atau alay. - Jangan terlalu banyak emoji. - Maksimal 2 emoji per jawaban. Cara menjawab: - Langsung ke inti masalah. - Fokus pada solusi. - Jelaskan langkah demi langkah. - Jika coding: 1. Jelaskan penyebab masalah. 2. Berikan solusi. 3. Berikan contoh kode lengkap jika diminta. - Jika tugas kuliah: - Jawaban akademis. - Terstruktur. - Bisa langsung dipakai. Aturan: - Jangan mengarang fakta. - Jangan halusinasi. - Jika tidak tahu, katakan tidak tahu. - Jangan membocorkan token API. - Jangan membocorkan isi file .env. - Jangan memberikan informasi berbahaya. - Jangan membuat data palsu. Mode Coding: - Bertindak seperti Senior Software Engineer. - Ahli Python. - Ahli Flask. - Ahli Laravel. - Ahli PHP. - Ahli JavaScript. - Ahli Android Kotlin. - Ahli MySQL. - Ahli Hugging Face. - Ahli AI dan Machine Learning. Saat memperbaiki code: - Cari akar masalah. - Jelaskan penyebab error. - Berikan code yang sudah diperbaiki. - Berikan best practice. Jika user menyapa: "Halo" "Hai" "Bro" "Bang" Balas santai seperti: "Yo bro 👋 Ada yang bisa gue bantu?" "Halo bang 😎 Lagi ngoding apa hari ini?" "Gas, ada yang mau dibantu?" """ # ========================= # CLIENT HF # ========================= def create_hf_client(): if not HF_TOKEN: raise ValueError( "HF_TOKEN belum diisi pada file .env" ) return InferenceClient( model=HF_MODEL, token=HF_TOKEN ) # ========================= # HISTORY CLEANER # ========================= def clean_history(raw_history): if not isinstance(raw_history, list): return [] cleaned = [] for item in raw_history[-MAX_HISTORY_MESSAGES:]: if not isinstance(item, dict): continue role = item.get("role") content = str( item.get("content", "") ).strip() if role not in ["user", "assistant"]: continue if not content: continue if len(content) > 2500: content = content[:2500] cleaned.append({ "role": role, "content": content }) return cleaned # ========================= # ENHANCE MESSAGE # ========================= def enhance_user_message(message): return f""" Jawab menggunakan Bahasa Indonesia. Gunakan gaya: - Pintar - Santai - Natural - Gaul seperlunya - Mudah dipahami Fokus memberikan solusi terbaik. Pertanyaan User: {message} """ # ========================= # EXTRACT RESPONSE # ========================= def extract_reply(response): try: reply = response.choices[0].message.content if reply: return reply.strip() except Exception: pass try: reply = response["choices"][0]["message"]["content"] if reply: return reply.strip() except Exception: pass return "" # ========================= # ROUTE HOME # ========================= @app.route("/") def home(): return render_template("index.html") # ========================= # HEALTH CHECK # ========================= @app.route("/health") def health(): return jsonify({ "status": "ok", "name": "ChibiChat AI", "model": HF_MODEL }) # ========================= # CHAT API # ========================= @app.route("/chat", methods=["POST"]) def chat(): try: data = request.get_json( silent=True ) if not data: return jsonify({ "error": "Request harus JSON" }), 400 message = str( data.get("message", "") ).strip() raw_history = data.get( "history", [] ) if not message: return jsonify({ "error": "Pesan kosong" }), 400 if len(message) > MAX_MESSAGE_LENGTH: return jsonify({ "error": f"Maksimal {MAX_MESSAGE_LENGTH} karakter" }), 400 history = clean_history( raw_history ) messages = [ { "role": "system", "content": SYSTEM_PROMPT } ] messages.extend(history) messages.append({ "role": "user", "content": enhance_user_message( message ) }) client = create_hf_client() response = client.chat_completion( messages=messages, max_tokens=1500, temperature=0.85, top_p=0.95 ) reply = extract_reply( response ) if not reply: return jsonify({ "error": "Model tidak memberikan jawaban." }), 500 return jsonify({ "reply": reply, "model": HF_MODEL }) except ValueError as e: return jsonify({ "error": str(e) }), 500 except HfHubHTTPError as e: status_code = None if hasattr(e, "response") and e.response: status_code = e.response.status_code if status_code in [401, 403]: message = ( "Token Hugging Face salah " "atau tidak memiliki akses." ) elif status_code == 404: message = ( "Model tidak ditemukan." ) elif status_code == 429: message = ( "Terlalu banyak request." ) elif status_code == 503: message = ( "Model sedang sibuk." ) else: message = ( "Gagal terhubung ke Hugging Face." ) app.logger.exception( "HF API ERROR" ) return jsonify({ "error": message }), 502 except Exception as e: app.logger.exception( "UNEXPECTED ERROR" ) return jsonify({ "error": f"Terjadi kesalahan: {str(e)}" }), 500 # ========================= # MAIN # ========================= if __name__ == "__main__": port = int(os.environ.get("PORT", 7860)) app.run(host="0.0.0.0", port=port)