""" Gradio Blocks UI for the multilingual RAG chatbot. Run: python3 app.py """ import subprocess import pathlib import gradio as gr from rag_pipeline import RAGPipeline if not pathlib.Path("faiss_index").exists(): print("FAISS index not found — running ingest.py...") subprocess.run(["python3", "ingest.py"], check=True) pipeline = RAGPipeline() DESCRIPTION = """ # 🌐 Multilingual RAG Chatbot Ask questions in **English**, **العربية (Arabic)**, or **Bahasa Melayu (Malay)**. Answers are grounded in the knowledge base. Retrieved source passages appear below each answer. """ EXAMPLES = [ ["What is retrieval-augmented generation?"], ["How does machine learning differ from deep learning?"], ["What are vector databases used for?"], ["ما هو الذكاء الاصطناعي؟"], ["كيف يعمل التعلم الآلي؟"], ["ما هي قواعد البيانات المتجهية؟"], ["Apakah itu kecerdasan buatan?"], ["Bagaimana pembelajaran mesin berfungsi?"], ["Apakah RAG dan kegunaannya?"], ] RTL_CSS = """ .message-bubble p, .message-bubble span, .prose p { unicode-bidi: plaintext; text-align: start; } [lang="ar"], .rtl-text { direction: rtl; text-align: right; font-family: 'Segoe UI', Tahoma, Arial, sans-serif; } .source-box { border-left: 3px solid #6366f1; padding-left: 0.75rem; margin: 0.5rem 0; font-size: 0.875rem; } .example-btn { font-size: 0.82rem !important; } """ JS_RTL = """ function applyRTL() { document.querySelectorAll('.message-bubble p, .prose p').forEach(el => { if (/[؀-ۿ]/.test(el.innerText || '')) { el.setAttribute('dir', 'rtl'); el.style.textAlign = 'right'; } }); } const observer = new MutationObserver(applyRTL); observer.observe(document.body, { childList: true, subtree: true }); applyRTL(); """ def format_sources(sources: list[dict]) -> str: if not sources: return "_No sources retrieved._" lines = [] for i, s in enumerate(sources, 1): snippet = s["content"].replace("\n", " ").strip() if len(snippet) > 300: snippet = snippet[:300] + "…" file_name = s["source"].split("/")[-1] lines.append(f"**[{i}] `{file_name}`**\n> {snippet}") return "\n\n".join(lines) def respond(message: str, history: list): if not message.strip(): yield history, "_Please enter a question._" return result = pipeline.ask(message) answer = result["answer"] sources_md = format_sources(result["sources"]) history = history + [ {"role": "user", "content": message}, {"role": "assistant", "content": answer}, ] yield history, sources_md def clear_all(): pipeline.chain.memory.clear() return [], "_Sources will appear here after your first question._" with gr.Blocks(title="Multilingual RAG Chatbot") as demo: gr.Markdown(DESCRIPTION) with gr.Row(): with gr.Column(scale=3): chatbot = gr.Chatbot( elem_id="chatbot", label="Chat", height=480, ) with gr.Row(): msg_box = gr.Textbox( placeholder="Type your question in English, العربية, or Bahasa Melayu…", show_label=False, scale=8, autofocus=True, ) send_btn = gr.Button("Send ➤", variant="primary", scale=1) with gr.Row(): clear_btn = gr.Button("🗑 Clear Chat", variant="secondary", size="sm") with gr.Column(scale=2): gr.Markdown("### 📄 Retrieved Sources") sources_box = gr.Markdown( value="_Sources will appear here after your first question._", ) with gr.Accordion("💡 Example Questions", open=True): with gr.Row(): with gr.Column(): gr.Markdown("**🇬🇧 English**") for ex in EXAMPLES[:3]: gr.Button(ex[0], elem_classes="example-btn").click( fn=lambda q=ex[0]: q, outputs=msg_box, ) with gr.Column(): gr.Markdown("**🇸🇦 العربية**") for ex in EXAMPLES[3:6]: gr.Button(ex[0], elem_classes="example-btn").click( fn=lambda q=ex[0]: q, outputs=msg_box, ) with gr.Column(): gr.Markdown("**🇲🇾 Bahasa Melayu**") for ex in EXAMPLES[6:]: gr.Button(ex[0], elem_classes="example-btn").click( fn=lambda q=ex[0]: q, outputs=msg_box, ) submit_inputs = [msg_box, chatbot] submit_outputs = [chatbot, sources_box] msg_box.submit(respond, submit_inputs, submit_outputs).then( fn=lambda: "", outputs=msg_box ) send_btn.click(respond, submit_inputs, submit_outputs).then( fn=lambda: "", outputs=msg_box ) clear_btn.click(clear_all, outputs=[chatbot, sources_box]) if __name__ == "__main__": demo.launch( server_name="0.0.0.0", server_port=7860, show_error=True, css=RTL_CSS, js=JS_RTL, )