| | import gradio as gr
|
| | from llama_cpp import Llama
|
| | from huggingface_hub import hf_hub_download
|
| | import os
|
| | import re
|
| |
|
| | try:
|
| | from voice import get_audio_file, VOICES
|
| | VOICE_ENABLED = True
|
| | except ImportError:
|
| | VOICE_ENABLED = False
|
| | VOICES = {}
|
| |
|
| | DEFAULT_SYSTEM_PROMPT = """You are Hiba (賴亘丞), meaning "Gift from God" in Arabic.
|
| | You are a warm, gentle AI companion for emotional support.
|
| |
|
| | RULES:
|
| | - Be empathetic and kind (Sabr).
|
| | - Keep responses SHORT (2-4 sentences).
|
| | - NEVER use hashtags.
|
| | - Be natural and warm."""
|
| |
|
| | MODEL_REPO = "TRADMSS/HIBA-7B-Soul"
|
| | MODEL_FILE = "hiba_q4_k_m.gguf"
|
| | llm = None
|
| |
|
| | def load_model():
|
| | global llm
|
| | if llm is None:
|
| | model_path = hf_hub_download(repo_id=MODEL_REPO, filename=MODEL_FILE)
|
| | llm = Llama(model_path=model_path, n_ctx=2048, n_threads=2, verbose=False)
|
| | return llm
|
| |
|
| | def clean_response(text):
|
| | """Remove training artifacts and tags from response."""
|
| |
|
| | text = re.sub(r'<think>.*?</think>', '', text, flags=re.DOTALL)
|
| |
|
| | text = re.sub(r'<\|.*?\|>', '', text)
|
| |
|
| | text = re.sub(r'\([^)]*\)', '', text)
|
| |
|
| | text = re.sub(r'\s+', ' ', text).strip()
|
| | return text
|
| |
|
| | def ask_hiba(message, history, system_prompt, voice_choice):
|
| | if not message.strip():
|
| | return history, None
|
| |
|
| | history = history or []
|
| | history.append([message, None])
|
| |
|
| | try:
|
| | model = load_model()
|
| | prompt = f"<|im_start|>system\n{system_prompt}<|im_end|>\n"
|
| | for user, bot in history[:-1]:
|
| | prompt += f"<|im_start|>user\n{user}<|im_end|>\n"
|
| | if bot:
|
| | prompt += f"<|im_start|>assistant\n{bot}<|im_end|>\n"
|
| | prompt += f"<|im_start|>user\n{message}<|im_end|>\n<|im_start|>assistant\n"
|
| |
|
| | output = model(
|
| | prompt,
|
| | max_tokens=250,
|
| | stop=["<|im_end|>"],
|
| | temperature=0.7,
|
| | echo=False
|
| | )
|
| |
|
| | raw_response = output['choices'][0]['text']
|
| | response = clean_response(raw_response)
|
| |
|
| | if not response:
|
| | response = "I am here with you."
|
| |
|
| | history[-1][1] = response
|
| |
|
| |
|
| | audio = None
|
| | if VOICE_ENABLED:
|
| | try:
|
| | audio = get_audio_file(response, voice_choice)
|
| | except Exception as e:
|
| | print(f"Voice error: {e}")
|
| |
|
| | return history, audio
|
| |
|
| | except Exception as e:
|
| | history[-1][1] = f"Error: {str(e)}"
|
| | return history, None
|
| |
|
| |
|
| | css = """
|
| | @import url('https://fonts.googleapis.com/css2?family=Newsreader:ital,wght@0,400;0,600;1,400&family=Inter:wght@400;500;600&display=swap');
|
| |
|
| | :root {
|
| | --bg: #0a0a0f;
|
| | --surface: #12121a;
|
| | --border: rgba(255,255,255,0.08);
|
| | --text: #f0f0f5;
|
| | --muted: #888;
|
| | --accent: #c9a66b;
|
| | --pink: #e8b4c8;
|
| | }
|
| |
|
| | * { font-family: 'Inter', sans-serif !important; }
|
| |
|
| | body, .gradio-container {
|
| | background: var(--bg) !important;
|
| | color: var(--text) !important;
|
| | max-width: 100% !important;
|
| | margin: 0 !important;
|
| | padding: 0 !important;
|
| | }
|
| |
|
| | footer { display: none !important; }
|
| |
|
| | /* Header */
|
| | .header {
|
| | text-align: center;
|
| | padding: 30px 20px;
|
| | border-bottom: 1px solid var(--border);
|
| | }
|
| |
|
| | .brand {
|
| | font-family: 'Newsreader', serif !important;
|
| | font-size: 36px;
|
| | font-weight: 600;
|
| | margin: 0;
|
| | background: linear-gradient(135deg, var(--accent), var(--pink));
|
| | -webkit-background-clip: text;
|
| | -webkit-text-fill-color: transparent;
|
| | }
|
| |
|
| | .tagline {
|
| | font-size: 14px;
|
| | color: var(--muted);
|
| | margin-top: 8px;
|
| | }
|
| |
|
| | /* Main Layout */
|
| | .main-area {
|
| | display: flex;
|
| | height: calc(100vh - 120px);
|
| | }
|
| |
|
| | /* Sidebar */
|
| | .sidebar {
|
| | width: 300px;
|
| | background: var(--surface);
|
| | border-right: 1px solid var(--border);
|
| | padding: 20px;
|
| | overflow-y: auto;
|
| | }
|
| |
|
| | .sidebar-title {
|
| | font-size: 12px;
|
| | font-weight: 600;
|
| | color: var(--accent);
|
| | text-transform: uppercase;
|
| | letter-spacing: 1px;
|
| | margin-bottom: 12px;
|
| | }
|
| |
|
| | /* Chat Area */
|
| | .chat-area {
|
| | flex: 1;
|
| | display: flex;
|
| | flex-direction: column;
|
| | padding: 20px;
|
| | }
|
| |
|
| | .chatbot {
|
| | flex: 1 !important;
|
| | overflow-y: auto !important;
|
| | background: transparent !important;
|
| | border: none !important;
|
| | }
|
| |
|
| | /* Input */
|
| | .input-row {
|
| | display: flex;
|
| | gap: 10px;
|
| | padding-top: 10px;
|
| | border-top: 1px solid var(--border);
|
| | }
|
| |
|
| | textarea {
|
| | background: var(--surface) !important;
|
| | border: 1px solid var(--border) !important;
|
| | color: var(--text) !important;
|
| | border-radius: 12px !important;
|
| | padding: 12px !important;
|
| | font-size: 15px !important;
|
| | }
|
| |
|
| | textarea:focus {
|
| | border-color: var(--accent) !important;
|
| | }
|
| |
|
| | /* Buttons */
|
| | .send-btn {
|
| | background: var(--accent) !important;
|
| | color: #000 !important;
|
| | border: none !important;
|
| | border-radius: 12px !important;
|
| | padding: 12px 24px !important;
|
| | font-weight: 600 !important;
|
| | }
|
| |
|
| | /* Footer */
|
| | .footer {
|
| | text-align: center;
|
| | padding: 15px;
|
| | color: var(--muted);
|
| | font-size: 12px;
|
| | border-top: 1px solid var(--border);
|
| | }
|
| |
|
| | .footer a {
|
| | color: var(--accent);
|
| | text-decoration: none;
|
| | }
|
| |
|
| | /* Audio */
|
| | audio { width: 100%; margin-top: 10px; }
|
| |
|
| | /* Accordion */
|
| | .accordion { background: var(--surface) !important; border: 1px solid var(--border) !important; }
|
| | """
|
| |
|
| |
|
| | voice_options = list(VOICES.keys()) if VOICES else ["woman"]
|
| |
|
| | with gr.Blocks(css=css, title="HIBA Developer Demo", fill_height=True) as demo:
|
| |
|
| |
|
| | gr.HTML("""
|
| | <div class="header">
|
| | <h1 class="brand">HIBA</h1>
|
| | <p class="tagline">A Gift from God 路 Developer Demo</p>
|
| | </div>
|
| | """)
|
| |
|
| | with gr.Row():
|
| |
|
| | with gr.Column(scale=1, min_width=280):
|
| | gr.HTML('<div class="sidebar-title">鈿欙笍 Developer Settings</div>')
|
| |
|
| | system_prompt = gr.Textbox(
|
| | label="System Prompt",
|
| | value=DEFAULT_SYSTEM_PROMPT,
|
| | lines=10,
|
| | max_lines=15,
|
| | placeholder="Customize HIBA's persona..."
|
| | )
|
| |
|
| | voice_choice = gr.Dropdown(
|
| | label="Voice",
|
| | choices=voice_options,
|
| | value="girl_sweet",
|
| | allow_custom_value=False
|
| | )
|
| |
|
| | gr.HTML('<br><div class="sidebar-title">馃摎 About</div>')
|
| | gr.Markdown("""
|
| | **HIBA-7B-Soul** is trained on 5,000+ emotional conversations.
|
| |
|
| | Use this demo to test different prompts and voices.
|
| |
|
| | [GitHub](https://github.com/boubli/HIBA) 路 [Website](https://boubli.github.io/HIBA/)
|
| | """)
|
| |
|
| |
|
| | with gr.Column(scale=2):
|
| | chatbot = gr.Chatbot(
|
| | elem_classes="chatbot",
|
| | show_label=False,
|
| | bubble_full_width=False,
|
| | avatar_images=(None, "https://huggingface.co/spaces/TRADMSS/HIBA-Demo/resolve/main/logo.png"),
|
| | height=500
|
| | )
|
| |
|
| | audio_output = gr.Audio(label="Voice Response", autoplay=True)
|
| |
|
| | with gr.Row(elem_classes="input-row"):
|
| | msg = gr.Textbox(
|
| | show_label=False,
|
| | placeholder="Message HIBA...",
|
| | container=False,
|
| | scale=4
|
| | )
|
| | send_btn = gr.Button("Send", elem_classes="send-btn", scale=1)
|
| |
|
| |
|
| | gr.HTML("""
|
| | <div class="footer">
|
| | Open Source 路 Therapeutic AI 路 Made with 鉂わ笍 by <a href="https://github.com/boubli">Youssef Boubli</a>
|
| | </div>
|
| | """)
|
| |
|
| |
|
| | send_btn.click(ask_hiba, [msg, chatbot, system_prompt, voice_choice], [chatbot, audio_output]).then(
|
| | lambda: "", None, [msg]
|
| | )
|
| | msg.submit(ask_hiba, [msg, chatbot, system_prompt, voice_choice], [chatbot, audio_output]).then(
|
| | lambda: "", None, [msg]
|
| | )
|
| |
|
| | if __name__ == "__main__":
|
| | demo.launch(server_name="0.0.0.0", server_port=7860)
|
| |
|